{"record":{"id":"d41848b79843b83c","repo":"github/copilot-sdk","slug":"factory-phase-titles-must-not-be-empty","errorCode":null,"errorMessage":"Factory phase titles must not be empty","messagePattern":"Factory phase titles must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodejs/src/factory.ts","lineNumber":468,"sourceCode":"        const maxNanoAiu = Math.round(limits.maxAiCredits * NANO_AIU_PER_AIU);\n        if (\n            !Number.isFinite(limits.maxAiCredits) ||\n            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> {","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/factory.ts#L450-L486","documentation":"validatePhases rejects factory phase definitions whose title is empty or consists only of whitespace. Phase titles identify stages of a factory and are used for display and uniqueness checks, so an empty title is considered invalid metadata and fails at defineFactory time.","triggerScenarios":"Calling defineFactory with a phase whose title is '' or contains only spaces/newlines (title.trim().length === 0). Also occurs when titles are derived from data (e.g. a missing i18n string or a field that failed to map) and end up as empty strings.","commonSituations":"Localization files missing a translation key resulting in ''; spreadsheet/JSON-driven phase generation with blank rows; refactoring that removed a default title; accidental whitespace-only strings from user input.","solutions":["Give every phase a non-empty title string with at least one non-whitespace character.","Validate titles before calling defineFactory, especially when they come from data files or user input.","Fix the data source (translation table, JSON, spreadsheet) that is producing empty titles.","Trim and check user-supplied titles at input boundaries."],"exampleFix":"// before\ndefineFactory({ name: 'build', phases: [{ title: '   ', steps: [] }] });\n// after\ndefineFactory({ name: 'build', phases: [{ title: 'Compile', steps: [] }] });","handlingStrategy":"validation","validationCode":"for (const p of phases) {\n  if (typeof p.title !== 'string' || p.title.trim().length === 0) throw new TypeError(`Phase title must be non-empty (got ${JSON.stringify(p.title)})`);\n}","typeGuard":"function hasTitle(p) { return typeof p.title === 'string' && p.title.trim().length > 0; }","tryCatchPattern":"try {\n  defineFactory(meta);\n} catch (e) {\n  if (e.message === 'Factory phase titles must not be empty') console.error('A phase is missing a title');\n  throw e;\n}","preventionTips":["Validate phase data at load time (i18n keys, spreadsheets, JSON)","Use zod/ajv schemas requiring non-empty strings for titles","Trim user-supplied titles at input boundaries","Never leave placeholder empty titles during scaffolding"],"tags":["validation","factory","metadata"],"backgroundTag":"empty-required-field","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"}