{"record":{"id":"0dc78cffb117a1bd","repo":"decolua/9router","slug":"kiro-tooluseevent-has-an-invalid-tooluseid","errorCode":null,"errorMessage":"Kiro toolUseEvent has an invalid toolUseId","messagePattern":"Kiro toolUseEvent has an invalid toolUseId","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/kiro.js","lineNumber":837,"sourceCode":"          state.totalContentLength += content.length;\n          emitDelta(controller, { reasoning_content: content });\n        }\n      } else if (eventType === \"codeEvent\" && typeof event.payload?.content === \"string\") {\n        state.hasCode = true;\n        state.totalContentLength += event.payload.content.length;\n        emitDelta(controller, { content: event.payload.content });\n      } else if (eventType === \"toolUseEvent\") {\n        state.sawToolUse = true;\n        const values = Array.isArray(event.payload) ? event.payload : [event.payload];\n        if (!values[0]) throw new Error(\"Kiro toolUseEvent is empty\");\n        for (const value of values) {\n          const name = typeof value?.name === \"string\" ? value.name.trim() : \"\";\n          if (!name) throw new Error(\"Kiro toolUseEvent is missing a tool name\");\n          let id;\n          if (value.toolUseId == null) {\n            id = `call_${created}_${state.tools.size + 1}`;\n          } else if (typeof value.toolUseId !== \"string\" || !value.toolUseId.trim()) {\n            throw new Error(\"Kiro toolUseEvent has an invalid toolUseId\");\n          } else {\n            id = value.toolUseId;\n          }\n          let tool = state.tools.get(id);\n          if (!tool) {\n            tool = { id, name };\n            state.tools.set(id, tool);\n            state.bufferedToolBytes += encoder.encode(id).byteLength + encoder.encode(name).byteLength + 32;\n            assertToolBufferBound();\n          } else if (tool.name !== name) {\n            throw new Error(\"Kiro tool name changed between fragments\");\n          }\n          appendToolInput(tool, value.input);\n        }\n      } else if (eventType === \"messageStopEvent\") {\n        state.explicitStop = true;\n        const reason = normalizeStopReason(\n          event.payload?.stopReason ?? event.payload?.stop_reason","sourceCodeStart":819,"sourceCodeEnd":855,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/kiro.js#L819-L855","documentation":"The executor accepts a toolUseId that is either null/undefined (it then synthesizes call_<created>_<n>) or a non-empty trimmed string. A toolUseId that is present but not a usable string — wrong type, empty string, or whitespace-only — is treated as a corrupted fragment and throws, since tool fragments are keyed by id and a bad id would break fragment correlation and deduplication.","triggerScenarios":"A toolUseEvent value with value.toolUseId set to a non-string (number, object), an empty string, or whitespace-only string. E.g. upstream sent toolUseId: 0 or toolUseId: \"\" in a continuation fragment.","commonSituations":"Upstream serializer type drift (numeric ids instead of strings); Kiro emitting placeholder empty ids during degraded responses; middleware/proxies rewriting event payloads and coercing id types.","solutions":["Retry the request once — transient corrupted id fragments are usually not reproducible","Capture the raw EventStream event to confirm the actual toolUseId type/value being received","If upstream now sends numeric ids, coerce instead of throwing: String(value.toolUseId).trim() with a length check","Update the executor's id handling if a Kiro API version change altered the id format"],"exampleFix":"// before: reject any non-string id\n} else if (typeof value.toolUseId !== 'string' || !value.toolUseId.trim()) {\n  throw new Error('Kiro toolUseEvent has an invalid toolUseId');\n}\n// after: coerce scalar ids to strings, only reject truly unusable values\n} else if (typeof value.toolUseId === 'string' && value.toolUseId.trim()) {\n  id = value.toolUseId.trim();\n} else if (typeof value.toolUseId === 'number' && Number.isFinite(value.toolUseId)) {\n  id = `call_${value.toolUseId}`;\n} else {\n  throw new Error('Kiro toolUseEvent has an invalid toolUseId');\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function hasValidToolUseId(value) {\n  return value?.toolUseId == null ||\n    (typeof value.toolUseId === 'string' && value.toolUseId.trim().length > 0);\n}","tryCatchPattern":"try {\n  return await streamKiro(req);\n} catch (e) {\n  if (/invalid toolUseId/.test(e.message) && attempts < 2) return retry(req);\n  throw e;\n}","preventionTips":["Retry once — corrupted id fragments are typically transient","Verify no proxy/middleware between you and Kiro rewrites or type-coerces event payloads","Check upstream changelogs for toolUseId format changes (e.g. numeric ids) and coerce proactively","Keep raw EventStream capture enabled at debug level for post-mortem of id corruption"],"tags":["kiro","tool-use","eventstream","type-validation","streaming"],"backgroundTag":"invalid-tool-use-id","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}