{"record":{"id":"6c8556feba3ea466","repo":"can1357/oh-my-pi","slug":"cannot-replace-built-in-composer-style-id","errorCode":null,"errorMessage":"Cannot replace built-in composer style \"${id}\"","messagePattern":"Cannot replace built-in composer style \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/tui/src/components/composer/registry.ts","lineNumber":47,"sourceCode":" * Whether a style paints its own row foreground.\n *\n * Extensions registered before `filledSurface` existed received undecorated\n * row text, so an omitted flag remains filled for extension-owned ids.\n */\nexport function isFilledComposerStyle(style: ComposerStyle): boolean {\n\treturn style.filledSurface ?? !isBuiltinComposerStyle(style.id);\n}\n\n/**\n * Register one extension-owned composer style for this process.\n *\n * Built-in ids and duplicate extension ids are rejected. The returned disposer\n * removes only this registration.\n */\nexport function registerComposerStyle(style: ComposerStyle): () => void {\n\tconst id = style.id.trim();\n\tif (id.length === 0 || id !== style.id) throw new TypeError(\"Composer style id must be a non-empty trimmed string\");\n\tif (isBuiltinComposerStyle(id)) throw new Error(`Cannot replace built-in composer style \"${id}\"`);\n\tif (extensionComposerStyles.has(id)) throw new Error(`Composer style \"${id}\" is already registered`);\n\textensionComposerStyles.set(id, style);\n\treturn () => {\n\t\tif (extensionComposerStyles.get(id) === style) extensionComposerStyles.delete(id);\n\t};\n}\n\n/** Style object for a composer shape; unknown ids fall back to `box`. */\nexport function getComposerStyle(id: EditorBorderStyle): ComposerStyle {\n\treturn extensionComposerStyles.get(id) ?? BUILTIN_COMPOSER_STYLES[id] ?? boxComposerStyle;\n}\n","sourceCodeStart":29,"sourceCodeEnd":59,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/tui/src/components/composer/registry.ts#L29-L59","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nregisterComposerStyle({ id: \"default\", ... })\n// after\nregisterComposerStyle({ id: \"my-default\", ... })","handlingStrategy":"validation","validationCode":"if (isBuiltinComposerStyle(style.id.trim())) {\n  throw new Error(`choose a non-builtin id, \"${style.id}\" is reserved`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  registerComposerStyle(style);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"Cannot replace built-in\")) {\n    registerComposerStyle({ ...style, id: `custom-${style.id}` });\n  } else throw err;\n}","preventionTips":["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"],"tags":["api-misuse","tui","naming-conflict"],"backgroundTag":"reserved-identifier","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}