{"record":{"id":"6cfbe7f5817abc11","repo":"github/copilot-sdk","slug":"factory-phase-title-phase-title-is-declared-m","errorCode":null,"errorMessage":"Factory phase title \"${phase.title}\" is declared more than once","messagePattern":"Factory phase title \"(.+?)\" is declared more than once","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodejs/src/factory.ts","lineNumber":471,"sourceCode":"            limits.maxAiCredits <= 0 ||\n            !Number.isSafeInteger(maxNanoAiu) ||\n            maxNanoAiu < 1\n        ) {\n            throw new Error(\n                'Factory limit \"maxAiCredits\" must be a positive, finite number that rounds to a safe positive integer nano-AIU ceiling'\n            );\n        }\n    }\n}\n\nfunction validatePhases(meta: FactoryMeta): void {\n    const titles = new Set<string>();\n    for (const phase of meta.phases) {\n        if (phase.title.trim().length === 0) {\n            throw new Error(\"Factory phase titles must not be empty\");\n        }\n        if (titles.has(phase.title)) {\n            throw new Error(`Factory phase title \"${phase.title}\" is declared more than once`);\n        }\n        titles.add(phase.title);\n    }\n}\n\n/**\n * Defines an extension-authored factory and returns an opaque registration handle.\n *\n * @experimental Part of the experimental Agent Factories surface and may\n * change or be removed in future SDK or CLI releases.\n */\nexport function defineFactory<\n    TArgs extends JsonValue = JsonValue,\n    TResult extends JsonValue | void = JsonValue | void,\n>(definition: FactoryDefinition<TArgs, TResult>): FactoryHandle<TArgs, TResult> {\n    // Snapshot before validating so post-registration mutation of the caller's\n    // object cannot slip past the authoring-boundary checks.\n    const meta = deepFreeze(structuredClone(definition.meta));","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/factory.ts#L453-L489","documentation":"validatePhases enforces that each factory phase title is unique, tracked via a Set during validation. Duplicate titles would make phases ambiguous for display and step association, so defineFactory rejects the definition. Note that comparison is on the exact title string, not the trimmed value.","triggerScenarios":"Calling defineFactory whose meta.phases array contains two phases with the identical title string. Common when phases are generated from data that repeats a label, or when copy-pasting phase blocks without renaming the title.","commonSituations":"Generated phase lists from configs or spreadsheets with repeated labels; copy-pasted phase definitions; merging phase arrays from multiple sources that use the same names; programmatic phase creation with a template title not updated per phase.","solutions":["Rename the duplicate phase so every title in meta.phases is unique.","Deduplicate or suffix generated titles programmatically before calling defineFactory.","Merge the duplicate phases into a single phase if they represent the same stage.","Validate titles for uniqueness in the upstream data source."],"exampleFix":"// before\ndefineFactory({ name: 'build', phases: [{ title: 'Compile' }, { title: 'Compile' }] });\n// after\ndefineFactory({ name: 'build', phases: [{ title: 'Compile' }, { title: 'Link' }] });","handlingStrategy":"validation","validationCode":"const seen = new Set();\nfor (const p of phases) {\n  if (seen.has(p.title)) throw new TypeError(`Duplicate phase title: \"${p.title}\"`);\n  seen.add(p.title);\n}","typeGuard":"function hasUniqueTitles(phases) { return new Set(phases.map(p => p.title)).size === phases.length; }","tryCatchPattern":"try {\n  defineFactory(meta);\n} catch (e) {\n  const m = /phase title \"(.+)\" is declared more than once/.exec(e.message);\n  if (m) console.error(`Rename duplicate phase: ${m[1]}`);\n  throw e;\n}","preventionTips":["Deduplicate or suffix generated phase titles before defineFactory","Add a uniqueness assertion in tests for phase metadata","Merge phases that legitimately share the same stage name"],"tags":["validation","factory","duplicate","metadata"],"backgroundTag":"schema-validation-failed","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}