Airtable Cobuilder is here! Learn more about our new no-code app creation feature, powered by AI on the Airtable Academy

Automation Script Returns Persistent "Unexpected End of JSON Input" Error

Topic Labels: Automations
330 0
Showing results for 
Search instead for 
Did you mean: 
6 - Interface Innovator
6 - Interface Innovator

Hello Airtable Community.  

I've reached my wits end and so has chatGPT 🙂  I am using the following scripting code in an Automation to extract some data from the record that triggered the automation and insert into an external API.  Most of it is working as expected until the very end of the first function where I keep getting 



"Error processing Airtable update:"
"Unexpected end of JSON input"



I've tried lots of logs to understand the input and everything appears in order to me.  JSON is coming back fine in each of the "console.logs" indicated in the code here.  An example here:


What could be causing this error?  Source code below:



// Function to escape HTML characters
function escapeHtml(input) {
    return input.replace(/</g, "&lt;").replace(/>/g, "&gt;");

// Airtable Script to trigger on "Terpene Tags" field update

// Airtable Base ID and Table Name

// Personal Access Token for Airtable

// External API endpoint
const EXTERNAL_API_KEY = 'XXX'; // Replace with your actual API key

// Relevant roomIds
const RELEVANT_ROOM_IDS = ['10650', '6454'];

// Airtable Script function to handle updates
async function onTerpeneTagsUpdate(event) {
    try {
        const recordId = event ? event.recordId : undefined;
        console.log('Record Id:', recordId);

        if (!recordId) {
            console.error('RecordId is missing. Exiting script.');

        // Fetch data from Airtable
        const airtableRecord = await fetchAirtableRecord(recordId);
        console.log('Airtable Record:', airtableRecord);

        // Check if 'fields' property exists before accessing it
        if (airtableRecord && airtableRecord.fields) {
            // Access 'fields' property safely

            // Now you can access specific properties of 'fields'
            const productName = airtableRecord.fields["ProductIdNumber"];
            const productTagsString = airtableRecord.fields["Terpene Names"];
            console.log("productName:", productName);
            console.log("productTags:", productTagsString);

            // Convert the comma-separated string to an array
            const productTags = productTagsString.split(',').map(tag => tag.trim());
            console.log("Terpene Tags As Array:", productTags);

            // Add more processing logic as needed
        } else {
            // Handle the case where 'fields' is undefined
            console.error("Error: 'fields' property is undefined");
            // You might want to handle this error appropriately, depending on your use case
            return; // Exit the function if 'fields' is undefined
        if (!airtableRecord || !airtableRecord.fields) {
            console.error('Airtable record not found or has missing fields. Exiting script.');

        // Use the new numeric field for productId
        const productId = airtableRecord.fields.ProductIdNumber;

        // Fetch data from /inventory endpoint
        const inventoryData = await fetchInventoryData();
        console.log("Inventory Data:", inventoryData);

        // Filter relevant packages (considering roomId for sale)
        const relevantPackages = inventoryData.filter(
            (package) =>
                package.fields.productId === productId &&

        // Build payload
        const payload = {
            packageIds: => p.fields.packageId),
            tags: airtableRecord.fields['Terpene Names'], // Assuming you have the "Terpene Names" field

        // Make API call to external endpoint
        await makeExternalAPICall(payload);

        console.log('Tags added successfully');
    } catch (error) {
        console.error('Error processing Airtable update:', error.message);

let parsedData; // Declare parsedData at a higher scope

// Function to safely parse JSON
function parseJSONSafe(jsonString) {
    try {
        return JSON.parse(jsonString);
    } catch (error) {
        console.error("Error parsing JSON:", error);
        return null;

// Updated fetchAirtableRecord function
async function fetchAirtableRecord(recordId) {
    const response = await fetch(
            method: 'GET',
            headers: {
                Authorization: `Bearer ${AIRTABLE_PERSONAL_ACCESS_TOKEN}`,

    console.log('Airtable API Response:', response.status, response.statusText);
    console.log('Airtable API Headers:', response.headers);

    if (!response.ok) {
        console.error(`Failed to fetch Airtable record: ${response.status} - ${response.statusText}`);
        return null;

    const responseBody = await response.text();
    console.log("Airtable Response Body:", responseBody);

    // Safely parse the JSON
    const parsedData = parseJSONSafe(responseBody);

    if (!parsedData || !parsedData.fields) {
        console.error("Error: Invalid Airtable record structure");
        return null;

    // Access the correct key for the record ID
    const id =;
    console.log("Record ID:", id);

    // Now you can use the "id" as needed

    // Return the parsed data
    return parsedData;

// Function to fetch data from /inventory endpoint
async function fetchInventoryData() {
    const response = await fetch('');
    const data = await response.json();
    return data.records;

// Function to make API call to external endpoint
async function makeExternalAPICall(payload) {
    const response = await fetch(EXTERNAL_API_ENDPOINT, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json-patch+json',
            'Authorization': `Bearer ${EXTERNAL_API_KEY}`, // Include your API key here
        body: JSON.stringify(payload),

    if (!response.ok) {
        throw new Error(`Failed to make external API call: ${response.status} - ${response.statusText}`);

// Example: Trigger this function when Airtable record is updated
const event = input.config(); // Use input.config() to get the input data from the automation



Any ideas?

0 Replies 0