{"record":{"id":"fd7c650b14ecc563","repo":"payloadcms/payload","slug":"document-cannot-be-its-own-parent","errorCode":null,"errorMessage":"Document cannot be its own parent","messagePattern":"Document cannot be its own parent","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/payload/src/hierarchy/hooks/collectionBeforeChange.ts","lineNumber":44,"sourceCode":"  ({ parentFieldName }: Args): CollectionBeforeChangeHook =>\n  async ({ collection, data, operation, originalDoc, req }) => {\n    // Determine the new parent ID\n    const newParentID =\n      data[parentFieldName] !== undefined ? data[parentFieldName] : originalDoc?.[parentFieldName]\n    const parentChanged =\n      operation === 'update' &&\n      data[parentFieldName] !== undefined &&\n      data[parentFieldName] !== originalDoc?.[parentFieldName]\n\n    // Validate circular references when parent is changing\n    if (parentChanged && newParentID) {\n      // Extract parent ID (could be plain ID or populated object with id)\n      const parentId =\n        typeof newParentID === 'object' && 'id' in newParentID ? newParentID.id : newParentID\n\n      // Prevent self-referential parent\n      if (parentId === (originalDoc?.id || data.id)) {\n        throw new Error('Document cannot be its own parent')\n      }\n\n      // Check for true circular references (loops in the chain), but allow\n      // moving into a child - that will be handled by afterChange reparenting\n      await validateNoCircularReference({\n        collection,\n        currentDocId: originalDoc?.id,\n        parentId,\n        req,\n      })\n    }\n\n    return data\n  }\n\n/**\n * Walks up the parent chain to detect true circular references (loops).\n * Does NOT throw when moving into a child - that case is handled by afterChange","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/hierarchy/hooks/collectionBeforeChange.ts#L26-L62","documentation":"Thrown by the hierarchy beforeChange hook when, during an update, the parent field is being changed and the resolved new parent id equals the document's own id. Self-parenting would create an immediate cycle, so it is rejected outright.","triggerScenarios":"PATCH/PUT or payload.update where data[parentFieldName] is set to the same value as the doc's own id, on a collection with hierarchy enabled.","commonSituations":"UI bug that prefills the parent picker with the current node; import/migration that copies a row's id into its parent column; client that posts the full doc including its existing parent id but shifts it to the self field.","solutions":["On the client, filter the current document out of the parent-selection options.","Before save, strip or rewrite data[parentFieldName] when it equals the doc id.","Validate in a beforeValidate hook and surface a user-friendly message.","Audit import scripts to never set parent = id."],"exampleFix":"// before\nawait payload.update({ collection, id, data: { parent: id } })\n\n// after\nif (data.parent === id) {\n  delete data.parent // or throw a friendly validation error\n}\nawait payload.update({ collection, id, data })","handlingStrategy":"validation","validationCode":"if (data[parentFieldName] === id) {\n  delete data[parentFieldName]\n}","typeGuard":"function isSelfParent(\n  id: string | number,\n  parent: unknown,\n): boolean {\n  const pid =\n    typeof parent === 'object' && parent !== null && 'id' in parent\n      ? (parent as { id: string | number }).id\n      : parent\n  return String(pid) === String(id)\n}","tryCatchPattern":"try {\n  await payload.update({ collection, id, data })\n} catch (err) {\n  if (err instanceof Error && /own parent/.test(err.message)) {\n    return { error: 'A folder cannot be its own parent' }\n  }\n  throw err\n}","preventionTips":["Filter the current document out of the parent picker options in the UI.","Strip self-referential parent ids in beforeValidate before they reach beforeChange."],"tags":["hierarchy","validation","before-change","circular"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}