gatsbyjs/gatsby · critical

Types implementing queryable interfaces must also implement

Error message

Types implementing queryable interfaces must also implement the `Node` interface. Check the type definition of ${incorrectTypes}.

What it means

Gatsby enforces that any object type implementing a queryable interface (one registered as queryable) must also implement the Node interface. Queryable interfaces participate in root-level queries and rely on node identity. The panic lists all violating type names in incorrectTypes.

Source

Thrown at packages/gatsby/src/schema/schema.js:1405

      queryableInterfaces.add(type.getTypeName())
    }
  })
  const incorrectTypes = new Set()
  schemaComposer.forEach(type => {
    if (type instanceof ObjectTypeComposer) {
      const interfaces = type.getInterfaces()
      if (
        interfaces.some(iface =>
          queryableInterfaces.has(iface.getTypeName())
        ) &&
        !type.hasInterface(`Node`)
      ) {
        incorrectTypes.add(type.getTypeName())
      }
    }
  })
  if (incorrectTypes.size) {
    report.panic(
      `Types implementing queryable interfaces must also implement the \`Node\` ` +
        `interface. Check the type definition of ` +
        `${Array.from(incorrectTypes)
          .map(t => `\`${t}\``)
          .join(`, `)}.`
    )
  }
}

const mergeFields = ({ typeComposer, fields }) =>
  Object.entries(fields).forEach(([fieldName, fieldConfig]) => {
    if (typeComposer.hasField(fieldName)) {
      typeComposer.extendField(fieldName, fieldConfig)
    } else {
      typeComposer.setField(fieldName, fieldConfig)
    }
  })

View on GitHub (pinned to 8b06340921)

Solutions

  1. Add Node to the implements list of each type listed in the error: `type MyType implements MyInterface & Node { ... }`.
  2. If the interface should not be queryable, remove the queryable extension from the interface definition.
  3. Review all createTypes calls and plugin schema extensions for interface usage on the listed types.

Example fix

// before
type MyType implements MyQueryableInterface {
  id: ID!
}

// after
type MyType implements MyQueryableInterface & Node {
  id: ID!
}
Defensive patterns

Strategy: validation

Validate before calling

// Check that types implementing queryable interfaces also implement Node
function validateQueryableInterfaces(typeDefs, queryableInterfaceNames) {
  const issues = []
  for (const iface of queryableInterfaceNames) {
    const re = new RegExp('type\\s+(\\w+)\\s+implements\\s+[^{]*' + iface + '[^{]*\\{', 'g')
    let match
    while ((match = re.exec(typeDefs))) {
      const implBlock = typeDefs.slice(match.index, match.index + 200)
      if (!/Node/.test(implBlock)) {
        issues.push(match[1] + ' implements ' + iface + ' but not Node')
      }
    }
  }
  return issues
}

Prevention

When it happens

Trigger: An object type `implements` a queryable interface (e.g. an interface with the nodeInterface or similar queryable extension) but does not also list Node in its implements clause. The check iterates all types, finds those with a queryable interface but without Node, and panics.

Common situations: A plugin defines a queryable interface and a custom type implements it without adding Node. Mixing custom interfaces with node types and forgetting the Node interface requirement. Schema stitching across plugins where one plugin's interface is queryable but consumer types omit Node.

Related errors


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