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

  1. Choose a unique, non-built-in id for your extension style
  2. If you want the built-in's look, clone it and register under a new id
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/6c8556feba3ea466. Report an issue: GitHub.