can1357/oh-my-pi · error · Error
Cannot replace built-in composer style "${id}"
Error message
Cannot replace built-in composer style "${id}" What it means
registerComposerStyle() protects built-in composer styles from being overridden. If a style's id matches a built-in id (per isBuiltinComposerStyle), registration throws instead of silently replacing the built-in.
Source
Thrown at packages/tui/src/components/composer/registry.ts:47
* Whether a style paints its own row foreground.
*
* Extensions registered before `filledSurface` existed received undecorated
* row text, so an omitted flag remains filled for extension-owned ids.
*/
export function isFilledComposerStyle(style: ComposerStyle): boolean {
return style.filledSurface ?? !isBuiltinComposerStyle(style.id);
}
/**
* Register one extension-owned composer style for this process.
*
* Built-in ids and duplicate extension ids are rejected. The returned disposer
* removes only this registration.
*/
export function registerComposerStyle(style: ComposerStyle): () => void {
const id = style.id.trim();
if (id.length === 0 || id !== style.id) throw new TypeError("Composer style id must be a non-empty trimmed string");
if (isBuiltinComposerStyle(id)) throw new Error(`Cannot replace built-in composer style "${id}"`);
if (extensionComposerStyles.has(id)) throw new Error(`Composer style "${id}" is already registered`);
extensionComposerStyles.set(id, style);
return () => {
if (extensionComposerStyles.get(id) === style) extensionComposerStyles.delete(id);
};
}
/** Style object for a composer shape; unknown ids fall back to `box`. */
export function getComposerStyle(id: EditorBorderStyle): ComposerStyle {
return extensionComposerStyles.get(id) ?? BUILTIN_COMPOSER_STYLES[id] ?? boxComposerStyle;
}
View on GitHub (pinned to 9690622007)
Solutions
- Choose a unique, non-built-in id for your extension style
- If you want the built-in's look, clone it and register under a new id
- Check the registry's built-in id list before picking an id
Example fix
// before
registerComposerStyle({ id: "default", ... })
// after
registerComposerStyle({ id: "my-default", ... }) Defensive patterns
Strategy: validation
Validate before calling
if (isBuiltinComposerStyle(style.id.trim())) {
throw new Error(`choose a non-builtin id, "${style.id}" is reserved`);
} Try / catch
try {
registerComposerStyle(style);
} catch (err) {
if (err instanceof Error && err.message.includes("Cannot replace built-in")) {
registerComposerStyle({ ...style, id: `custom-${style.id}` });
} else throw err;
} Prevention
- Namespace extension ids (e.g. "myplugin-default") to avoid collisions with built-ins
- Check the built-in style list before picking an id
- When cloning a built-in style, always rename the id
When it happens
Trigger: Calling registerComposerStyle with a style whose trimmed id equals a built-in style id (e.g. reusing a core style name for a custom style).
Common situations: Trying to monkey-patch a built-in style by registering one with the same id; copying an existing style object and forgetting to change its id.
Related errors
- Composer style id must be a non-empty trimmed string
- Composer style "${id}" is already registered
- No messages to continue from
- Cannot continue from message role: assistant
- Gemini Files API cannot delete a handle from another provide
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/6c8556feba3ea466.
Report an issue: GitHub.