BigPizzaV3/CodexPlusPlus · error

任务树包含不存在的父节点

Error message

任务树包含不存在的父节点

What it means

During the parent-chain walk, if a node's parent is not null and not a key in the byId map of already-validated nodes, the result references a parent node that was never defined. This error enforces referential integrity of the tree: every non-null parent must point to a node present in `nodes`.

Solutions

  1. Collect every parent value from the JSON and diff against the set of node ids to find the dangling reference.
  2. Retry with instructions that every referenced parent id must appear in the nodes array itself.
  3. Add an explicit root node with parent:null and re-parent orphans under it.
  4. Fix id typos/casing so parent references match existing node ids exactly.

Example fix

// before: n2 references missing 'nX'
{"nodes":[{"id":"n1","parent":null},{"id":"n2","parent":"nX"}]}
// after
{"nodes":[{"id":"n1","parent":null},{"id":"n2","parent":"n1"}]}
Defensive patterns

Strategy: validation

Validate before calling

function hasNoDanglingParents(org){ const ids=new Set((org?.nodes||[]).map(n=>n?.id)); return (org?.nodes||[]).every(n => n?.parent===null || ids.has(n.parent)); }

Type guard

function parentExists(n, allIds){ return n?.parent===null || (typeof n?.parent==='string' && allIds.has(n.parent)); }

Try / catch

try { const org = parseOrganization(text, messages, requestId); } catch (e) { if (e.message === '任务树包含不存在的父节点') { /* re-ask model to include all referenced nodes */ } else throw e; }

Prevention

When it happens

Trigger: A node has parent:"n9" but no node with id "n9" exists in the array (typo, dropped node, id renamed); the model invents a parent id like "root" without including it as a node; a node was filtered out upstream but its children remain. Check: `current.parent!==null&&!byId.has(current.parent)`.

Common situations: Model truncates its JSON (nodes list cut off) leaving dangling parent references; prompt asks the model to link to a 'root' the model assumes exists implicitly; node ids regenerated between attempts so old parent ids no longer match; case-sensitivity mismatches ("N1" vs "n1").

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/32e108b775f6a981. Report an issue: GitHub.

Appendix: source

Thrown at tools/conversation-canvas/organize.mjs:30

export function validateOrganization(value,messages,requestId) {
  if(value?.requestId!==requestId||!Array.isArray(value.nodes)||!value.nodes.length)throw Error('整理结果格式不完整');
  const sources=new Set(messages.map(m=>m.id)), nodes=[], ids=new Set();
  for(const n of value.nodes){
    if(!n||typeof n.id!=='string'||!/^[a-zA-Z0-9_-]{1,80}$/.test(n.id)||ids.has(n.id))throw Error('整理结果节点 ID 无效');
    if(!['main','branch'].includes(n.lane)||!Array.isArray(n.sources)||!n.sources.length||!n.sources.every(id=>sources.has(id)))throw Error('整理结果包含无效来源');
    for(const key of ['title','summary','description','status'])if(typeof n[key]!=='string'||!n[key].trim()||n[key].length>12000)throw Error('整理结果缺少节点描述');
    if(n.parent!==null&&typeof n.parent!=='string')throw Error('整理结果父节点无效');
    ids.add(n.id);nodes.push({id:n.id,parent:n.parent,lane:n.lane,title:n.title,summary:n.summary,description:n.description,status:n.status,sources:[...new Set(n.sources)]});
  }
  const roots=nodes.filter(n=>n.parent===null),byId=new Map(nodes.map(n=>[n.id,n]));
  if(roots.length!==1||roots[0].lane!=='main')throw Error('任务树必须有且只有一个主目标根节点');
  const checked=new Set();
  for(const node of nodes){
    const path=new Set();let current=node;
    while(current&&!checked.has(current.id)){
      if(path.has(current.id))throw Error('任务树存在循环关联');
      path.add(current.id);
      if(current.parent!==null&&!byId.has(current.parent))throw Error('任务树包含不存在的父节点');
      current=current.parent===null?null:byId.get(current.parent);
    }
    for(const id of path)checked.add(id);
  }
  const currentNodeId=value.currentNodeId??null;
  if(currentNodeId!==null&&!byId.has(currentNodeId))throw Error('当前推进节点不存在');
  return {schemaVersion:2,currentNodeId,nodes};
}

export function parseOrganization(text,messages,requestId) {
  const blocks=[...text.matchAll(/```(?:json)?\s*([\s\S]*?)```/g)].map(m=>m[1]);
  for(const raw of [...blocks,text.trim()]){
    let value;try{value=JSON.parse(raw);}catch{continue;}
    if(value?.requestId===requestId)return validateOrganization(value,messages,requestId);
  }
  throw Error('侧边对话未返回可识别的整理结果');
}

View on GitHub (pinned to b1ed92e5e4)