{"record":{"id":"9ce7e2b8bc6c40d1","repo":"moeru-ai/airi","slug":"invalid-json-for-componentprops-error-as-error","errorCode":null,"errorMessage":"Invalid JSON for componentProps: ${(error as Error).message}","messagePattern":"Invalid JSON for componentProps: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/renderer/stores/tools/builtin/widgets.ts","lineNumber":216,"sourceCode":"    ...input,\n    windowSize: normalizeWidgetWindowSizeInput(input.windowSize),\n  }\n}\n\nexport function normalizeComponentProps(raw?: string | Record<string, any>) {\n  if (raw === undefined || raw === null)\n    return {}\n\n  if (typeof raw === 'string') {\n    const payload = raw.trim()\n    if (!payload)\n      return {}\n    try {\n      const parsed = JSON.parse(payload)\n      return typeof parsed === 'object' && parsed !== null ? parsed : {}\n    }\n    catch (error) {\n      throw new Error(`Invalid JSON for componentProps: ${(error as Error).message}`)\n    }\n  }\n\n  if (typeof raw === 'object')\n    return raw\n\n  return {}\n}\n\nfunction resolveWindowSize(\n  componentName: string | undefined,\n  componentProps: Record<string, any>,\n  windowSize?: WidgetWindowSize,\n) {\n  const explicitWindowSize = normalizeWidgetWindowSize(windowSize)\n  if (explicitWindowSize)\n    return explicitWindowSize\n","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/renderer/stores/tools/builtin/widgets.ts#L198-L234","documentation":"Thrown by normalizeComponentProps when raw is a non-empty string that JSON.parse rejects. The componentProps field accepts either a pre-parsed object (passed through) or a JSON string (parsed); a malformed JSON string surfaces the underlying SyntaxError message.","triggerScenarios":"A widget tool call passed componentProps as a string that is not valid JSON — trailing comma, unquoted keys, single quotes, stray characters, or partial JSON. Only the string branch parses; objects pass through untouched.","commonSituations":"LLM-generated tool call emitted hand-written object-literal text instead of JSON; user typed componentProps manually with JS object syntax; copy-paste introduced smart quotes or a trailing comma.","solutions":["Pass componentProps as a real object from the caller rather than a string when possible.","If a string is required, ensure it is strict JSON (double quotes, no trailing commas).","Validate/repair the JSON before calling normalizeComponentProps (e.g. JSON5 parse as a softer fallback).","Show the parse error to the LLM/tool-caller so it can re-emit valid JSON."],"exampleFix":"// before\nnormalizeComponentProps(\"{ foo: 'bar', }\")\n\n// after\nnormalizeComponentProps('{ \"foo\": \"bar\" }')\n// or pass an object directly:\nnormalizeComponentProps({ foo: 'bar' })","handlingStrategy":"validation","validationCode":"function isStrictJsonString(raw: string): boolean {\n  if (raw.trim().length === 0) return true\n  try { JSON.parse(raw); return true } catch { return false }\n}","typeGuard":"function isParsableComponentProps(raw: unknown): boolean {\n  if (raw == null) return true\n  if (typeof raw === 'object') return true\n  if (typeof raw === 'string') {\n    if (raw.trim() === '') return true\n    try { JSON.parse(raw); return true } catch { return false }\n  }\n  return false\n}","tryCatchPattern":"try {\n  normalizeComponentProps(raw)\n} catch (e) {\n  // surface the parse error to the tool-caller / LLM and request valid JSON\n}","preventionTips":["Pass componentProps as an object, not a JSON string, from typed callers.","Validate the JSON string before the widget tool executes.","Consider a tolerant parser (JSON5) as a fallback then re-serialize as strict JSON."],"tags":["widgets","json","parsing","validation","llm-tool-call"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}