{"record":{"id":"ac75f5768157aa64","repo":"can1357/oh-my-pi","slug":"widget-content-missing","errorCode":null,"errorMessage":"Widget content missing","messagePattern":"Widget content missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/modes/controllers/extension-ui-controller.ts","lineNumber":360,"sourceCode":"\t#removeHookWidget(widgets: Map<string, ExtensionUiComponent>, key: string): void {\n\t\tconst existing = widgets.get(key);\n\t\texisting?.dispose?.();\n\t\twidgets.delete(key);\n\t}\n\n\t#createHookWidget(content: ExtensionWidgetContent): ExtensionUiComponent {\n\t\tif (Array.isArray(content)) {\n\t\t\tconst container = new Container();\n\t\t\tfor (const line of content.slice(0, MAX_WIDGET_LINES)) {\n\t\t\t\tcontainer.addChild(new Text(line, 1, 0));\n\t\t\t}\n\t\t\tif (content.length > MAX_WIDGET_LINES) {\n\t\t\t\tcontainer.addChild(new Text(theme.fg(\"muted\", \"... (widget truncated)\"), 1, 0));\n\t\t\t}\n\t\t\treturn container;\n\t\t}\n\t\tif (content === undefined) {\n\t\t\tthrow new Error(\"Widget content missing\");\n\t\t}\n\t\treturn content(this.ctx.ui, theme);\n\t}\n\n\t#rebuildHookWidgets(): void {\n\t\tthis.#renderHookWidgetContainer(this.ctx.hookWidgetContainerAbove, this.#hookWidgetsAbove, true, true);\n\t\tthis.#renderHookWidgetContainer(this.ctx.hookWidgetContainerBelow, this.#hookWidgetsBelow, false, false);\n\t\tthis.ctx.ui.requestRender();\n\t}\n\n\t#renderHookWidgetContainer(\n\t\tcontainer: Container,\n\t\twidgets: Map<string, ExtensionUiComponent>,\n\t\tspacerWhenEmpty: boolean,\n\t\tleadingSpacer: boolean,\n\t): void {\n\t\tcontainer.clear();\n","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/controllers/extension-ui-controller.ts#L342-L378","documentation":"The extension UI widget hook registered with setHookWidget provided neither static content nor a render function. #createHookWidget expects the widget's `content` to be either an array of renderables or a function (content(this.ctx.ui, theme)); when it is undefined the widget cannot be rendered and this error is thrown. It indicates a malformed extension widget registration rather than a rendering problem.","triggerScenarios":"An extension/hook calls setHookWidget with a widget object whose `content` field is undefined — e.g. the hook computed content conditionally and returned undefined, or passed an object with a misspelled/missing content key.","commonSituations":"A third-party extension built against an older widget API where content was optional; an extension that returns undefined from an async lookup (e.g. failed fetch for widget data) and passes it straight through; a typo like `{ ctx }` instead of `{ content }`.","solutions":["Fix the extension to always supply `content` — either a renderable array or a `(ui, theme) => ...` function","In the extension, fall back to placeholder content (e.g. an empty container) when data is unavailable instead of undefined","Disable or update the offending extension to a version compatible with the current widget API","Guard the extension's widget registration with a check that content is defined before calling setHookWidget"],"exampleFix":"// before\nwidget.setContent(data?.renderable); // may be undefined\n// after\nwidget.setContent(data?.renderable ?? [new Text(theme.fg('muted', 'no data'), 1, 0)]);","handlingStrategy":"validation","validationCode":"// Before registering an extension widget\nif (widget.content === undefined) {\n  throw new Error('Extension widget must define content (renderables or render function)');\n}\nextensionUi.setHookWidget(widget);","typeGuard":"function hasWidgetContent(w: { content?: unknown }): w is { content: NonNullable<unknown> } {\n  return w.content !== undefined;\n}","tryCatchPattern":"try {\n  extensionUi.setHookWidget(widget);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Widget content missing') {\n    logger.warn('Extension widget rejected: no content', { id: widget.id });\n  }\n}","preventionTips":["Always initialize widget.content with a fallback renderable","Version-check extension APIs before registering widgets","Never pass through possibly-undefined values from async data fetches into content"],"tags":["extensions","widgets","api-misuse"],"backgroundTag":"missing-widget-content","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}