gatsbyjs/gatsby · error · Error
createNode must be a function, was ${typeof createNode}
Error message
createNode must be a function, was ${typeof createNode} What it means
createRemoteFileNode requires `createNode` to be a function because it must register the downloaded file as a Gatsby node. This check fires after the createNodeId check and rejects any non-function (undefined, null, object). Without a working createNode, the file would be downloaded but never appear in the Gatsby data layer.
Source
Thrown at packages/gatsby-source-filesystem/src/create-remote-file-node.js:120
createNode,
getCache,
parentNodeId = null,
auth = {},
httpHeaders = {},
createNodeId,
ext = null,
name = null,
}) {
// validation of the input
// without this it's notoriously easy to pass in the wrong `createNodeId`
// see gatsbyjs/gatsby#6643
if (typeof createNodeId !== `function`) {
throw new Error(
`createNodeId must be a function, was ${typeof createNodeId}`
)
}
if (typeof createNode !== `function`) {
throw new Error(`createNode must be a function, was ${typeof createNode}`)
}
if (typeof getCache === `function`) {
// use cache of this plugin and not cache of function caller
cache = getCache(`gatsby-source-filesystem`)
}
if (typeof cache !== `object`) {
throw new Error(
`Neither "cache" or "getCache" was passed. getCache must be function that return Gatsby cache, "cache" must be the Gatsby cache, was ${typeof cache}`
)
}
// Check if we already requested node for this remote file
// and return stored promise if we did.
if (processingCache[url]) {
return processingCache[url]
}
if (!url || isWebUri(url) === undefined) {View on GitHub (pinned to 8b06340921)
Solutions
- Pass the bound action: const { createNode } = actions; then createRemoteFileNode({ url, createNode, createNodeId, cache }).
- Confirm the sourceNodes hook signature destructures actions: exports.sourceNodes = async ({ actions, createNodeId, cache }) => { ... }.
- Forward createNode through any wrapper function explicitly rather than relying on closure capture.
- Verify typeof createNode === 'function' at the boundary of your wrapper.
Example fix
// before
const { createNodeId, cache } = helpers
await createRemoteFileNode({ url, createNodeId, cache })
// after
const { createNodeId, cache, actions: { createNode } } = helpers
await createRemoteFileNode({ url, createNodeId, cache, createNode }) Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof createNode !== 'function') {
throw new TypeError('createNode missing; destructure it from actions')
} Type guard
const isCreateNode = (v) => typeof v === 'function'
Try / catch
try { await createRemoteFileNode(opts) } catch (e) { if (/createNode must be a function/.test(e.message)) { reporter.panic('actions.createNode not forwarded') } throw e } Prevention
- Pull createNode from actions: const { createNode } = actions
- Pass the full helpers + actions objects through wrapper functions
- Lint against using createRemoteFileNode without a createNode binding in scope
When it happens
Trigger: Omitting createNode from the options; passing actions instead of actions.createNode; passing createNode: undefined from a broken destructuring; calling createRemoteFileNode from a context where actions were not captured.
Common situations: Destructuring only `createNodeId` and `cache` from helpers but forgetting `actions.createNode`; refactoring that moves createNode into a closure that is out of scope; examples in documentation that abbreviate the call.
Related errors
- Neither "cache" or "getCache" was passed. getCache must be f
- createNodeId must be a function, was ${typeof createNodeId}
- Neither "cache" or "getCache" was passed. getCache must be f
- createNode must be a function, was ${typeof createNode}
- You've passed something other than string to the exclude arr
AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13).
Data as JSON: /api/errors/34b497c2de25babb.
Report an issue: GitHub.