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

  1. Pass the bound action: const { createNode } = actions; then createRemoteFileNode({ url, createNode, createNodeId, cache }).
  2. Confirm the sourceNodes hook signature destructures actions: exports.sourceNodes = async ({ actions, createNodeId, cache }) => { ... }.
  3. Forward createNode through any wrapper function explicitly rather than relying on closure capture.
  4. 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

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


AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13). Data as JSON: /api/errors/34b497c2de25babb. Report an issue: GitHub.