can1357/oh-my-pi · error · TypeError
Composer style id must be a non-empty trimmed string
Error message
Composer style id must be a non-empty trimmed string
What it means
registerComposerStyle() validates extension composer style ids: the id must be non-empty after trimming and must already be trimmed. If `style.id.trim()` differs from `style.id` (leading/trailing whitespace) or trims to empty, a TypeError is thrown at registration time.
Source
Thrown at packages/tui/src/components/composer/registry.ts:46
/**
* 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
- Trim the id before registering: pass `id: rawId.trim()`
- Ensure the id is non-empty after trimming
- Fail fast on config parsing: validate ids at load time
Example fix
// before
registerComposerStyle({ id: name, ...style })
// after
registerComposerStyle({ id: name.trim(), ...style }) Defensive patterns
Strategy: validation
Validate before calling
function assertValidStyleId(id: string): void {
const t = id.trim();
if (t.length === 0 || t !== id) throw new TypeError(`invalid composer style id: ${JSON.stringify(id)}`);
} Type guard
function isTrimmedNonEmpty(id: string): boolean {
const t = id.trim();
return t.length > 0 && t === id;
} Try / catch
try {
registerComposerStyle(style);
} catch (err) {
if (err instanceof TypeError && err.message.includes("non-empty trimmed")) {
style = { ...style, id: style.id.trim() || fallbackId() };
registerComposerStyle(style);
} else throw err;
} Prevention
- Always construct ids with `.trim()` from raw input
- Validate style ids at config-load time, not at registration
- Never build ids from multi-line template strings
When it happens
Trigger: Calling registerComposerStyle with a ComposerStyle whose `id` is "", " ", "my-style ", or " my-style" — any id with surrounding whitespace or empty.
Common situations: Building id strings dynamically (e.g. from user input or config) that pick up whitespace; forgetting to set an id at all; template strings that end in a newline.
Related errors
- Gemini Files API delete requires a valid file name
- maxAgents must be a positive integer
- Unsupported vault:// file op: ${rawOp}
- Unsupported vault:// vault op: ${rawOp}
- vault:// path resolution only supports plain filesystem path
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b28fe1853cf12f9b.
Report an issue: GitHub.