gatsbyjs/gatsby · error · Error

The plugin "${pluginName}" deleted a node of a type owned by

Error message

The plugin "${pluginName}" deleted a node of a type owned by another plugin.

The node type "${internalNode.internal.type}" is owned by "${previouslyRecordedOwnerName}".

The node object passed to "deleteNode":

${JSON.stringify(internalNode, null, 4)}

The plugin deleting the node:

${JSON.stringify(plugin, null, 4)}

What it means

Symmetric to node creation, the type-owners reducer guards node deletion: if a plugin tries to deleteNode a node whose internal.type is owned by a different plugin, it throws. Children of a deleted node are exempt (action.isRecursiveChildrenDelete skips the check) so owners can cascade-delete descendants, but a foreign plugin cannot delete another plugin's typed nodes directly.

Source

Thrown at packages/gatsby/src/redux/reducers/type-owners.ts:86

  action: ActionsUnion
): IGatsbyState["typeOwners"] => {
  switch (action.type) {
    case `DELETE_NODE`: {
      const { plugin, payload: internalNode } = action

      if (plugin && internalNode && !action.isRecursiveChildrenDelete) {
        const pluginName = plugin.name

        const previouslyRecordedOwnerName = typeOwners.typesToPlugins.get(
          internalNode.internal.type
        )

        if (
          internalNode &&
          previouslyRecordedOwnerName &&
          previouslyRecordedOwnerName !== pluginName
        ) {
          throw new Error(stripIndent`
            The plugin "${pluginName}" deleted a node of a type owned by another plugin.

            The node type "${
              internalNode.internal.type
            }" is owned by "${previouslyRecordedOwnerName}".

            The node object passed to "deleteNode":

            ${JSON.stringify(internalNode, null, 4)}

            The plugin deleting the node:

            ${JSON.stringify(plugin, null, 4)}
        `)
        }
      }

      return typeOwners

View on GitHub (pinned to 8b06340921)

Solutions

  1. Only delete nodes your plugin owns; for relationships, use createParentChildLink and let the owner manage lifecycle.
  2. If you genuinely need to remove a foreign node, have the owning plugin expose an API or signal to delete it.
  3. Verify you are not dispatching deleteNode in a loop over getNodes() that includes other plugins' nodes.

Example fix

// before
getNodes().filter(n => n.internal.type === `SourceXPost`).forEach(deleteNode) // run by wrong plugin
// after - let the owning plugin handle its own deletions, or filter to your own type
getNodes().filter(n => n.internal.type === `MyType`).forEach(n => actions.deleteNode({ node: n }))
Defensive patterns

Strategy: validation

Validate before calling

// Only delete nodes of types your plugin owns.
function deleteOwnedOnly(actions, state, pluginName, node) {
  const owner = state.typeOwners?.typesToPlugins?.get(node.internal.type)
  if (owner && owner !== pluginName) {
    throw new Error(`Refusing to delete foreign node type ${node.internal.type} (owned by ${owner})`)
  }
  actions.deleteNode({ node })
}

Type guard

function ownsNodeType(node, pluginName, state) {
  const owner = state?.typeOwners?.typesToPlugins?.get(node?.internal?.type)
  return !owner || owner === pluginName
}

Prevention

When it happens

Trigger: Plugin B calls deleteNode on a node whose internal.internal.type is owned by plugin A (and isRecursiveChildrenDelete is false). The reducer looks up typesToPlugins.get(type) and compares to the deleting plugin's name.

Common situations: A plugin trying to clean up nodes it did not create (e.g. a sync plugin deleting stale CMS nodes); incorrect parent/child wiring causing a plugin to attempt direct deletion of foreign nodes; copy-pasted delete logic from another plugin.

Related errors


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