Challenges accessing data from Airtable database in Node.js Alexa skill

I’m developing an Alexa Skill with ASK-SDK v2 for Node.js that needs to retrieve data from an Airtable database. When users ask about a character’s name, the skill should query the database and return a simple description.

However, during my testing in the Skill Builder, even when the JSON input shows an ER_SUCCESS_MATCH, the output displays the recognized value as UNDEFINED. I hear the message, “I can’t find UNDEFINED in my database,” regardless of the character name I inquire about.

I suspect the skill is not properly connecting to the Airtable base, even though I believe I have entered the correct API key and Airtable parameters in my code. I would expect an ‘IN AIRTABLE GET’ entry to appear in my AWS Lambda logs, between ‘IN CHARACTER HANDLER’ and ‘END RequestID’, but instead, I’m going directly from ‘IN CHARACTER HANDLER’ to ‘END RequestID’, indicating that the Airtable database isn’t being accessed.

Here’s the entire Index.js code (excluding the Skill number, API Key, and character name column key). Can anyone identify any issues? Thank you for your assistance!

'use strict';
const Alexa = require('ask-sdk');
const https = require('https');

const APP_ID = "amzn1.ask.skill.xxxxxxxxxxxxxxxxxxxx";

const EmptyHandler = {
    canHandle(handlerInput) {
        return false;
    },
    handle(handlerInput, error) {
        return handlerInput.responseBuilder
                .speak()
                .reprompt()
                .getResponse();
    }
};

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === "LaunchRequest";
    },
    handle(handlerInput, error) {
        console.log("IN LAUNCH REQUEST");
        return handlerInput.responseBuilder
            .speak("Accessing the Skill. Ask me any character's name.")
            .reprompt("Go ahead, start asking.")
            .getResponse();
  },
};

const CharacterHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === "IntentRequest" &&
               handlerInput.requestEnvelope.request.intent.name === "CharacterIntent";
    },
    handle(handlerInput, error) {
        console.log("IN CHARACTER HANDLER");

        var spokenValue = getSpokenValue(handlerInput.requestEnvelope, "character");
        var resolvedValues = getResolvedValues(handlerInput.requestEnvelope, "character");

        //NO MATCHES FOUND
        if (resolvedValues === undefined) {
            return handlerInput.responseBuilder
                   .speak("I can't find " + spokenValue + " in my database." + getRandomQuestion())
                   .reprompt("" + spokenValue + " is not in my database. " + getRandomQuestion())
                   .getResponse();
        }
        //ONLY ONE MATCH FOUND
        else if (resolvedValues.length === 1) {
            var filter = "&filterByFormula=%7BName%7D%3D%22" + encodeURIComponent(resolvedValues[0].value.name) + "%22";

            return new Promise((resolve) => {
                airtableGet("appXXXXXXXXXX", "Character", filter, (record) => {
                    console.log("AIRTABLE RECORD = " + JSON.stringify(record));
                    var speechText = "Accessing to " + spokenValue + "<break time='.5s'/>" + record.records[0].fields.VoiceDescription;

                    console.log("RESPONSE BUILDER = " + JSON.stringify(handlerInput.responseBuilder));

                    resolve(handlerInput.responseBuilder
                       .speak(speechText)
                       .reprompt(getRandomQuestion())
                       .getResponse());
                });
            });
        }
        //MORE THAN ONE MATCH FOUND. DISAMBIGUATE.
        else if (resolvedValues.length > 1) {
            var valuesString = getValuesString(resolvedValues);

            return handlerInput.responseBuilder
                   .speak("You asked for " + spokenValue + ", but I have different matches for that name. Did you mean " + valuesString + "?")
                   .reprompt("Which one do you mean: " + valuesString + "?")
                   .getResponse();
        }
  }
};

// Remaining code omitted for brevity

Does anyone see what’s wrong with my implementation?

I’ve hit this exact issue with voice interfaces before. Your slot resolution logic is the problem.

You’re assuming that if resolvedValues exists, then resolvedValues[0].value.name will too. That’s not always true - slot resolution returns arrays with different structures depending on how Alexa matched the input.

Throw some debug logging right after you get the resolved values:

console.log("SPOKEN VALUE:", spokenValue);
console.log("RESOLVED VALUES:", JSON.stringify(resolvedValues));

I’m betting you’ll see that resolvedValues has items, but the structure’s different than you expect. Could be resolvedValues[0].name instead of resolvedValues[0].value.name, or maybe the value property just isn’t there.

Also double-check your slot type setup in the developer console. If entity resolution isn’t configured right, you might get resolved values that don’t match your Airtable records.

You’re not hitting the Airtable call because your condition check fails and takes the “no matches” path, even though Alexa technically resolved the slot.

The issue’s in your slot resolution logic. You’re checking if resolvedValues === undefined but then jumping straight to resolvedValues[0].value.name in the Airtable filter without validating it properly. Your slot resolution probably finds a match (so it’s not undefined), but the resolved value structure isn’t what you think it is. I ran into the same thing - slot resolution worked fine, but I was grabbing the wrong property from the resolved value object. First, log the entire resolvedValues object to see what you’re actually getting. Also double-check that your entity resolution model in the Alexa Developer Console has the right synonyms mapped to your character names. Since you’re not seeing the ‘IN AIRTABLE GET’ log, the Promise chain isn’t running - your slot resolution check is failing silently.

This topic was automatically closed 4 days after the last reply. New replies are no longer allowed.