{"record":{"id":"fe2d848c31069629","repo":"moeru-ai/airi","slug":"id-is-required-to-update-a-widget","errorCode":null,"errorMessage":"id is required to update a widget.","messagePattern":"id is required to update a widget\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/renderer/stores/tools/builtin/widgets.ts","lineNumber":275,"sourceCode":"      const componentProps = normalizeComponentProps(input.componentProps)\n      const sanitizedComponentProps = sanitizeComponentPropsForDispatch(input.componentName, componentProps)\n      const windowSize = resolveWindowSize(input.componentName, sanitizedComponentProps, input.windowSize)\n      const ttlMs = input.ttlSeconds ? Math.floor(input.ttlSeconds * 1000) : 0\n      const id = await invokers.addWidget({\n        id: normalizedId,\n        componentName: input.componentName,\n        componentProps: sanitizedComponentProps,\n        size: input.size ?? 'm',\n        ...(input.alwaysOnTop === undefined ? {} : { alwaysOnTop: input.alwaysOnTop }),\n        ...(windowSize === undefined ? {} : { windowSize }),\n        ttlMs,\n      })\n\n      return `Spawned widget${id ? ` (${id})` : ''}.`\n    }\n    case 'update': {\n      if (!normalizedId)\n        throw new Error('id is required to update a widget.')\n\n      const componentProps = normalizeComponentProps(input.componentProps)\n      const sanitizedComponentProps = sanitizeComponentPropsForDispatch(input.componentName, componentProps)\n      const windowSize = resolveWindowSize(input.componentName, sanitizedComponentProps, input.windowSize)\n      await invokers.updateWidget({\n        id: normalizedId,\n        componentProps: sanitizedComponentProps,\n        ...(input.alwaysOnTop === undefined ? {} : { alwaysOnTop: input.alwaysOnTop }),\n        ...(windowSize === undefined ? {} : { windowSize }),\n      })\n\n      return `Updated widget (${normalizedId}).`\n    }\n    case 'remove': {\n      if (!normalizedId)\n        throw new Error('id is required to remove a widget.')\n\n      await invokers.removeWidget({ id: normalizedId })","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/renderer/stores/tools/builtin/widgets.ts#L257-L293","documentation":"Thrown by executeWidgetAction when action='update' but no widget id can be resolved. normalizedId is computed as input.id?.trim() || undefined, so an empty or whitespace id becomes undefined and fails the guard at widgets.ts:275. The update action must target an existing widget by id, since updateWidget IPC requires the id to locate the live widget.","triggerScenarios":"Calling executeWidgetAction({ action: 'update' }) with id omitted, empty, or whitespace. Passing the stage_widgets tool an update turn where the model left id blank because it assumed update-by-componentName.","commonSituations":"An LLM confuses update (needs id) with spawn (id optional) and sends update without the widget id returned from the prior spawn; a caller reuses a spawn payload object for an update but drops the id field.","solutions":["Pass the id returned by the prior spawn call when issuing an update.","If you only know componentName, spawn a new widget instead, or track the id from the spawn result string 'Spawned widget (ID).'","For LLM-driven flows, include the spawned widget id in the conversation context so the model can echo it on update."],"exampleFix":"// before\nawait executeWidgetAction({ action: 'update', id: '  ', componentProps: { city: 'Osaka' } })\n// after\nawait executeWidgetAction({ action: 'update', id: 'w1', componentProps: { city: 'Osaka' } })","handlingStrategy":"validation","validationCode":"const normalizedId = input.id?.trim()\nif (input.action === 'update' && !normalizedId)\n  throw new Error('id is required to update a widget.')","typeGuard":"function hasUpdateId(input) {\n  return input.action !== 'update' || Boolean(input.id?.trim())\n}","tryCatchPattern":"try {\n  await executeWidgetAction(input)\n} catch (e) {\n  if (e.message.includes('id is required to update'))\n    // re-fetch widget id from prior spawn result\n  throw e\n}","preventionTips":["Persist the id returned by spawn ('Spawned widget (ID).') in caller state.","In LLM flows, include spawned widget ids in the conversation context for later updates."],"tags":["widget","validation","electron","stage-tamagotchi","ipc"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}