can1357/oh-my-pi · error · TypeError
Composer shape "${id}" must have a label
Error message
Composer shape "${id}" must have a label What it means
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.
Source
Thrown at packages/coding-agent/src/extensibility/extensions/loader.ts:246
this.runtime.flagValues.set(name, options.default);
}
}
registerMessageRenderer<T>(customType: string, renderer: MessageRenderer<T>): void {
this.extension.messageRenderers.set(customType, renderer as MessageRenderer);
}
registerAssistantThinkingRenderer(renderer: AssistantThinkingRenderer): void {
this.extension.assistantThinkingRenderers.push(renderer);
}
registerComposerShape(definition: ComposerShapeDefinition): void {
const id = definition.style.id;
if (id.length === 0 || id !== id.trim()) {
throw new TypeError("Composer shape id must be a non-empty trimmed string");
}
if (definition.label.trim().length === 0) {
throw new TypeError(`Composer shape "${id}" must have a label`);
}
if (isBuiltinComposerStyle(id)) {
throw new Error(`Cannot replace built-in composer shape "${id}"`);
}
this.extension.composerShapes.set(id, definition);
}
getFlag(name: string): boolean | string | undefined {
if (!this.extension.flags.has(name)) return undefined;
return this.runtime.flagValues.get(name);
}
sendMessage<T = unknown>(
message: CustomMessagePayload<T>,
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
): void {
this.runtime.sendMessage(message, options);
}View on GitHub (pinned to 9690622007)
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.
Example fix
// before
api.registerComposerShape({ style: { id: 'cards' }, label: config.label ?? '', ... });
// after
api.registerComposerShape({ style: { id: 'cards' }, label: config.label ?? 'Cards', ... }); Defensive patterns
Strategy: validation
Validate before calling
const label = rawLabel?.trim();
if (!label) throw new Error('composer shape label required');
api.registerComposerShape({ style: { id }, label, ... }); Type guard
function hasLabel(def: ComposerShapeDefinition): boolean {
return typeof def.label === 'string' && def.label.trim().length > 0;
} Try / catch
try {
api.registerComposerShape(def);
} catch (err) {
if (err instanceof TypeError && err.message.includes('must have a label')) {
def.label = def.style.id; // fallback label
api.registerComposerShape(def);
} else throw err;
} Prevention
- 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.
When it happens
Trigger: An extension registers a composer shape with label: '' or label: ' ', or a label sourced from missing i18n/config data.
Common situations: 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.
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
- Composer shape id must be a non-empty trimmed string
- Cannot replace built-in composer shape "${id}"
- Invalid service tier "${String(tier)}" for family "${String(
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f59b1d5348d2117a.
Report an issue: GitHub.