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
- Collect every parent value from the JSON and diff against the set of node ids to find the dangling reference.
- Retry with instructions that every referenced parent id must appear in the nodes array itself.
- Add an explicit root node with parent:null and re-parent orphans under it.
- 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
- Instruct the model: 'every non-null parent id must appear as an id in the nodes array'.
- Check output completeness — truncated JSON is a common cause of dangling parents.
- Keep id generation deterministic so ids match across retries.
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)