github/copilot-sdk · error
Factory phase title " " is declared more than once
Error message
Factory phase title "${phase.title}" is declared more than once What it means
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.
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.
Example fix
// before
defineFactory({ name: 'build', phases: [{ title: 'Compile' }, { title: 'Compile' }] });
// after
defineFactory({ name: 'build', phases: [{ title: 'Compile' }, { title: 'Link' }] }); Defensive patterns
Strategy: validation
Validate before calling
const seen = new Set();
for (const p of phases) {
if (seen.has(p.title)) throw new TypeError(`Duplicate phase title: "${p.title}"`);
seen.add(p.title);
} Type guard
function hasUniqueTitles(phases) { return new Set(phases.map(p => p.title)).size === phases.length; } Try / catch
try {
defineFactory(meta);
} catch (e) {
const m = /phase title "(.+)" is declared more than once/.exec(e.message);
if (m) console.error(`Rename duplicate phase: ${m[1]}`);
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Factory phase titles must not be empty
- Factory limit "timeoutSeconds" must be a positive, finite…
- Factory limit "timeoutSeconds" must not exceed
- Factory limit "maxAiCredits" must be a positive, finite…
- Duplicate factory name
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/6cfbe7f5817abc11.
Report an issue: GitHub.
Appendix: source
Thrown at nodejs/src/factory.ts:471
limits.maxAiCredits <= 0 ||
!Number.isSafeInteger(maxNanoAiu) ||
maxNanoAiu < 1
) {
throw new Error(
'Factory limit "maxAiCredits" must be a positive, finite number that rounds to a safe positive integer nano-AIU ceiling'
);
}
}
}
function validatePhases(meta: FactoryMeta): void {
const titles = new Set<string>();
for (const phase of meta.phases) {
if (phase.title.trim().length === 0) {
throw new Error("Factory phase titles must not be empty");
}
if (titles.has(phase.title)) {
throw new Error(`Factory phase title "${phase.title}" is declared more than once`);
}
titles.add(phase.title);
}
}
/**
* Defines an extension-authored factory and returns an opaque registration handle.
*
* @experimental Part of the experimental Agent Factories surface and may
* change or be removed in future SDK or CLI releases.
*/
export function defineFactory<
TArgs extends JsonValue = JsonValue,
TResult extends JsonValue | void = JsonValue | void,
>(definition: FactoryDefinition<TArgs, TResult>): FactoryHandle<TArgs, TResult> {
// Snapshot before validating so post-registration mutation of the caller's
// object cannot slip past the authoring-boundary checks.
const meta = deepFreeze(structuredClone(definition.meta));View on GitHub (pinned to cd8cf15dc3)