{"record":{"id":"f59b1d5348d2117a","repo":"can1357/oh-my-pi","slug":"composer-shape-id-must-have-a-label","errorCode":null,"errorMessage":"Composer shape \"${id}\" must have a label","messagePattern":"Composer shape \"(.+?)\" must have a label","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/extensions/loader.ts","lineNumber":246,"sourceCode":"\t\t\tthis.runtime.flagValues.set(name, options.default);\n\t\t}\n\t}\n\n\tregisterMessageRenderer<T>(customType: string, renderer: MessageRenderer<T>): void {\n\t\tthis.extension.messageRenderers.set(customType, renderer as MessageRenderer);\n\t}\n\n\tregisterAssistantThinkingRenderer(renderer: AssistantThinkingRenderer): void {\n\t\tthis.extension.assistantThinkingRenderers.push(renderer);\n\t}\n\n\tregisterComposerShape(definition: ComposerShapeDefinition): void {\n\t\tconst id = definition.style.id;\n\t\tif (id.length === 0 || id !== id.trim()) {\n\t\t\tthrow new TypeError(\"Composer shape id must be a non-empty trimmed string\");\n\t\t}\n\t\tif (definition.label.trim().length === 0) {\n\t\t\tthrow new TypeError(`Composer shape \"${id}\" must have a label`);\n\t\t}\n\t\tif (isBuiltinComposerStyle(id)) {\n\t\t\tthrow new Error(`Cannot replace built-in composer shape \"${id}\"`);\n\t\t}\n\t\tthis.extension.composerShapes.set(id, definition);\n\t}\n\n\tgetFlag(name: string): boolean | string | undefined {\n\t\tif (!this.extension.flags.has(name)) return undefined;\n\t\treturn this.runtime.flagValues.get(name);\n\t}\n\n\tsendMessage<T = unknown>(\n\t\tmessage: CustomMessagePayload<T>,\n\t\toptions?: { triggerTurn?: boolean; deliverAs?: \"steer\" | \"followUp\" | \"nextTurn\" },\n\t): void {\n\t\tthis.runtime.sendMessage(message, options);\n\t}","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/extensions/loader.ts#L228-L264","documentation":"After validating the id, registerComposerShape requires definition.label to contain non-whitespace content (label.trim().length > 0). An empty or whitespace-only label throws a TypeError naming the shape id.","triggerScenarios":"An extension registers a composer shape with label: '' or label: '   ', or a label sourced from missing i18n/config data.","commonSituations":"Localization file missing the label key so the lookup returns empty string; config value not yet loaded when the extension registers; developer passed a placeholder empty label while scaffolding.","solutions":["Provide a non-empty label string for the composer shape.","Fall back to a default label when config/i18n data is missing: label ?? 'My Shape'.","Trim and check the label before calling registerComposerShape and surface a clearer extension-level error."],"exampleFix":"// before\napi.registerComposerShape({ style: { id: 'cards' }, label: config.label ?? '', ... });\n// after\napi.registerComposerShape({ style: { id: 'cards' }, label: config.label ?? 'Cards', ... });","handlingStrategy":"validation","validationCode":"const label = rawLabel?.trim();\nif (!label) throw new Error('composer shape label required');\napi.registerComposerShape({ style: { id }, label, ... });","typeGuard":"function hasLabel(def: ComposerShapeDefinition): boolean {\n  return typeof def.label === 'string' && def.label.trim().length > 0;\n}","tryCatchPattern":"try {\n  api.registerComposerShape(def);\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes('must have a label')) {\n    def.label = def.style.id; // fallback label\n    api.registerComposerShape(def);\n  } else throw err;\n}","preventionTips":["Always supply a default/fallback label (?? 'Untitled').","Check that i18n/config keys exist before using them as labels.","Trim labels sourced from user config.","Fail fast in your own code with a clearer error naming the missing key."],"tags":["validation","extensions","composer"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}