{"record":{"id":"a65fb59714065ec1","repo":"can1357/oh-my-pi","slug":"composer-style-id-is-already-registered","errorCode":null,"errorMessage":"Composer style \"${id}\" is already registered","messagePattern":"Composer style \"(.+?)\" is already registered","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/tui/src/components/composer/registry.ts","lineNumber":48,"sourceCode":" *\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":30,"sourceCodeEnd":59,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/tui/src/components/composer/registry.ts#L30-L59","documentation":"registerComposerStyle() rejects duplicate registrations: if an extension style with the same id is already present in the extension map, it throws instead of overwriting. Built-in ids are rejected separately (a different error).","triggerScenarios":"Calling registerComposerStyle twice with the same extension style id without disposing the first registration.","commonSituations":"Hot-reload re-running an extension's registration code; a plugin loaded twice; forgetting to call the disposer returned by a previous registration before re-registering.","solutions":["Call the disposer returned by the first registerComposerStyle call before re-registering","Guard registration so it runs only once per id","Use a unique id per extension/feature"],"exampleFix":"// before\nregisterComposerStyle(style)\nregisterComposerStyle(style) // throws\n// after\nconst dispose = registerComposerStyle(style)\n// ...\ndispose() // then re-register safely\nregisterComposerStyle(style)","handlingStrategy":"try-catch","validationCode":"// track your own registrations to avoid duplicates\nconst registered = new Set<string>();\nfunction safeRegister(style: ComposerStyle) {\n  if (registered.has(style.id)) return;\n  registered.add(style.id);\n  disposers.set(style.id, registerComposerStyle(style));\n}","typeGuard":null,"tryCatchPattern":"const disposers = new Map<string, () => void>();\nfunction registerOnce(style: ComposerStyle) {\n  disposers.get(style.id)?.();\n  disposers.set(style.id, registerComposerStyle(style));\n}","preventionTips":["Store the disposer returned by registerComposerStyle and call it before re-registering","Make extension registration idempotent (guard against hot-reload double-runs)","Give each extension a unique id per feature"],"tags":["api-misuse","tui","duplicate-key"],"backgroundTag":"duplicate-registration","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}