coleam00/Archon · error

node artifact id collision: distinct producers both map to f

Error message

node artifact id collision: distinct producers both map to filename segment '${stem}'

What it means

writeNodeArtifact persists each workflow node's output as a markdown file whose filename comes from safeSegment(nodeId) (plus a loop digest for loop iterations). safeSegment can collapse distinct node ids onto the same filename segment (e.g. `a.b` and `a_b`), so before writing, the function reads the artifact owner recorded in the sidecar meta file; if a different producer already owns that path it throws instead of overwriting the other node's artifact.

Source

Thrown at packages/workflows/src/artifacts-index.ts:129

  const parsedParams = nodeArtifactWriteParamsSchema.parse(params);
  const nodesDir = join(artifactsDir, NODES_SUBDIR);
  await mkdir(nodesDir, { recursive: true });
  const owner: ArtifactOwner = {
    nodeId: parsedParams.nodeId,
    ...(parsedParams.loopGroupPath !== undefined
      ? { loopGroupPath: parsedParams.loopGroupPath }
      : {}),
  };
  const stem = artifactStem(owner);
  const metaPath = join(nodesDir, `${stem}.meta.json`);

  // Collision guard: top-level safeSegment() can collapse distinct node ids (for
  // example `a.b` and `a_b`), and loop digests retain an ownership check rather
  // than assuming their hash alone is authoritative. Compare the complete
  // producer identity and fail loudly instead of overwriting another artifact.
  const priorOwner = await readArtifactOwner(metaPath);
  if (priorOwner !== undefined && !sameArtifactOwner(priorOwner, owner)) {
    throw new Error(
      `node artifact id collision: distinct producers both map to filename segment '${stem}'`
    );
  }

  const relPath = join(NODES_SUBDIR, `${stem}.md`);
  await writeFile(join(artifactsDir, relPath), outputText, 'utf8');
  const meta: NodeArtifact = {
    nodeId: parsedParams.nodeId,
    outputType: parsedParams.outputType,
    ...(parsedParams.loopGroupPath !== undefined
      ? { loopGroupPath: parsedParams.loopGroupPath }
      : {}),
    path: relPath,
    runId: parsedParams.runId,
    producedAt: parsedParams.producedAt,
    size: Buffer.byteLength(outputText, 'utf8'),
    ...(parsedParams.sessionId !== undefined ? { sessionId: parsedParams.sessionId } : {}),
  };

View on GitHub (pinned to 0773b97458)

Solutions

  1. Rename one of the colliding nodes in the workflow YAML so their ids sanitize to distinct filename segments (avoid mixing '.' and '_' variants of the same name).
  2. Delete stale artifacts for the run's output directory if the collision comes from leftovers of a previous, differently-shaped run.
  3. If it occurs on resume after renaming a node, clear the run's artifacts directory or start a fresh run so owner records reset.
  4. Reproduce with the workflow's node-id list through safeSegment locally to confirm which pair collapses, then adjust ids accordingly.

Example fix

# before: two nodes in one workflow
- id: step.one
- id: step_one
# after: distinct sanitized segments
- id: step-one
- id: step-summary
Defensive patterns

Strategy: validation

Validate before calling

const segment = (id: string) => id.toLowerCase().replace(/[^a-z0-9]+/g, '_');
const seen = new Set<string>();
for (const node of workflow.nodes) {
  const s = segment(node.id);
  if (seen.has(s)) throw new Error(`Node ids ${[...seen].find(x => segment(x) === s)} and ${node.id} collide in artifact filenames`);
  seen.add(s);
}

Try / catch

try {
  await writeNodeArtifact(artifactsDir, owner, stem, outputText);
} catch (e) {
  if (/node artifact id collision/i.test(e?.message ?? '')) {
    logError(`Artifact filename segment '${stem}' is claimed by two producers; rename a node id.`);
  } else throw e;
}

Prevention

When it happens

Trigger: Two distinct node ids in the same run normalize to the same filename segment under safeSegment (characters like '.' vs '_' collapsed, or case/whitespace folding), and the second node writes its artifact to the already-claimed metaPath while a different owner is recorded.

Common situations: Workflow authors naming nodes `step.one` and `step_one` (or similar near-identical ids) in one workflow; a loop node whose digest coincides with another node's id segment after sanitization; a renamed node in a resumed run colliding with artifacts written by the old id; hand-edited artifacts leaving a stale owner file.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/6965fcfd760391c0. Report an issue: GitHub.