{"record":{"id":"c7e36d82ee8e905f","repo":"n8n-io/n8n","slug":"functionname-is-only-callable-on-type-have","errorCode":null,"errorMessage":"${functionName}() is only callable on type \"${haveFunction[0].typeName}\"","messagePattern":"(.+?)\\(\\) is only callable on type \"(.+?)\"","errorType":"exception","errorClass":"ExpressionExtensionError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/expression-runtime/src/extensions/extend.ts","lineNumber":159,"sourceCode":"\t// No type specific or generic function found. Check to see if\n\t// any types have a function with that name. Then throw an error\n\t// letting the user know the available types.\n\tif (!foundFunction) {\n\t\tcheckIfValueDefinedOrThrow(input, functionName);\n\t\tconst haveFunction = EXTENSION_OBJECTS.filter((v) => functionName in v.functions);\n\t\tif (!haveFunction.length) {\n\t\t\t// This shouldn't really be possible but we should cover it anyway\n\t\t\tthrow new ExpressionExtensionError(`Unknown expression function: ${functionName}`);\n\t\t}\n\n\t\tif (haveFunction.length > 1) {\n\t\t\tconst lastType = `\"${haveFunction.pop()!.typeName}\"`;\n\t\t\tconst typeNames = `${haveFunction.map((v) => `\"${v.typeName}\"`).join(', ')}, and ${lastType}`;\n\t\t\tthrow new ExpressionExtensionError(\n\t\t\t\t`${functionName}() is only callable on types ${typeNames}`,\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new ExpressionExtensionError(\n\t\t\t\t`${functionName}() is only callable on type \"${haveFunction[0].typeName}\"`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (foundFunction.type === 'native') {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-return\n\t\treturn foundFunction.function.apply(input, args);\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-return\n\treturn foundFunction.function(input, args);\n}\n\nexport function extendOptional(\n\tinput: unknown,\n\tfunctionName: string,\n\t// eslint-disable-next-line @typescript-eslint/no-restricted-types","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/expression-runtime/src/extensions/extend.ts#L141-L177","documentation":"Same dispatch logic as 325, but the method exists on EXACTLY ONE extension type. The error names that single type so the caller knows what to coerce to.","triggerScenarios":"Calling a type-specific method on the wrong type — e.g. `.isEven()` on a string, or `.toDate()` on a number that lacks the extension.","commonSituations":"Upstream delivered a different type than expected (string where a number was assumed, or vice-versa); JSON values whose types changed after a node upgrade.","solutions":["Coerce the value to the named type before calling (e.g. `.toNumber().isEven()`, `.toString().toDate()`).","Verify upstream node output types with a quick test or Set node.","Switch to a method defined for the actual runtime type."],"exampleFix":"// before (value arrives as string)\n{{ $json.amount.isEven() }}\n// after\n{{ $json.amount.toNumber().isEven() }}","handlingStrategy":"type-guard","validationCode":"if (typeof $json.amount !== 'number') {\n  throw new Error('Expected number for isEven(); got ' + typeof $json.amount);\n}\nreturn $json;","typeGuard":"const isNumber = (v: unknown): v is number => typeof v === 'number';","tryCatchPattern":null,"preventionTips":["Coerce with `.toNumber()` / `.toString()` to land on the single supported type.","Verify the upstream node's output schema.","Switch to a method defined for the actual runtime type."],"tags":["expression","type-mismatch","dispatch"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}