{"record":{"id":"586c33efa441d097","repo":"nocobase/nocobase","slug":"head-node-not-found-in-workflow-this-execution","errorCode":null,"errorMessage":"head node not found in workflow (#${this.execution.workflowId})","messagePattern":"head node not found in workflow \\(#(.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts","lineNumber":387,"sourceCode":"      return await this.run(node, input, { rerun: true });\n    } finally {\n      this.rerunContext = null;\n      this.leaveRunningState();\n    }\n  }\n\n  private getRerunNode(nodeId?: string | number) {\n    if (nodeId != null) {\n      const node = this.nodesMap.get(nodeId) || this.nodes.find((item) => String(item.id) === String(nodeId));\n      if (!node) {\n        throw new Error(`node (#${nodeId}) not found in workflow (#${this.execution.workflowId})`);\n      }\n      return node;\n    }\n\n    const head = this.nodes.find((item) => !item.upstream);\n    if (!head) {\n      throw new Error(`head node not found in workflow (#${this.execution.workflowId})`);\n    }\n    return head;\n  }\n\n  private getRerunInput(node: FlowNodeModel) {\n    if (!node.upstream) {\n      return { result: this.execution.context };\n    }\n\n    const upstreamJob = this.jobsMapByNodeKey[node.upstream.key];\n    if (!upstreamJob) {\n      throw new Error(`upstream job of node (#${node.id}) not found in execution (#${this.execution.id})`);\n    }\n\n    return upstreamJob;\n  }\n\n  private async exec(","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/nocobase/nocobase/blob/fa42722fefe44265490dff2c27d79e2882bce4fa/packages/plugins/@nocobase/plugin-workflow/src/server/Processor.ts#L369-L405","documentation":"When rerun is called without an explicit nodeId, getRerunNode derives the rerun start point from the workflow's head node (the node with no upstream). If no such node exists — the workflow graph is cyclic or has multiple disconnected entry nodes — it throws because rerun cannot determine where to start.","triggerScenarios":"processor.rerun({}) on a workflow whose node list contains no node with item.upstream falsy — corrupted node graph, all nodes have upstream links (cycle), or nodes failed to load so the list is incomplete/empty.","commonSituations":"Manually edited workflow node relationships creating a cycle; import/copy of a workflow losing the head node; data corruption in flow_nodes.upstreamId; rerunning an execution whose workflow nodes were never loaded (nodes array empty in this pass).","solutions":["Inspect flow_nodes for the workflow: exactly one node should have upstreamId null; fix broken upstreamId values.","Call rerun with an explicit valid nodeId instead of relying on head detection.","Rebuild/duplicate the workflow in the UI if its graph is corrupted.","Verify nodes loaded (execution.workflow.nodes non-empty) before rerunning."],"exampleFix":"// before\nawait processor.rerun({});\n// after\nconst head = execution.workflow.nodes.find((n) => !n.upstream);\nif (!head) {\n  console.error(`workflow ${execution.workflowId} has no head node; fix node graph`);\n} else {\n  await processor.rerun({ nodeId: head.id });\n}","handlingStrategy":"validation","validationCode":"const nodes = await workflow.getNodes();\nconst head = nodes.find((n) => !n.upstream);\nif (!head) {\n  throw new Error(`workflow ${workflow.id} graph is corrupted: no head node (check flow_nodes.upstreamId for cycles)`);\n}","typeGuard":"function hasSingleHead(nodes: FlowNodeModel[]): boolean {\n  return nodes.filter((n) => !n.upstream).length === 1;\n}","tryCatchPattern":"try {\n  await processor.rerun({});\n} catch (e) {\n  if (/head node not found/.test(e.message)) {\n    logger.error(`workflow ${execution.workflowId} has no head node; repair graph or pass explicit nodeId`);\n    return null;\n  }\n  throw e;\n}","preventionTips":["Never edit flow_nodes.upstreamId directly; use the workflow canvas so invariants hold.","Assert every workflow has exactly one upstream-less node after import/copy.","If the graph is uncertain, always pass an explicit nodeId to rerun.","Add a lint/audit script that detects cycles and missing heads in stored workflows."],"tags":["workflow","rerun","graph-corruption"],"backgroundTag":"workflow-node-not-found","analyzedSha":"fa42722fefe44265490dff2c27d79e2882bce4fa","analyzedAt":"2026-09-01T00:54:31.202Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}