{"record":{"id":"5a98c4db25297d35","repo":"decolua/9router","slug":"kiro-tooluseevent-is-empty","errorCode":null,"errorMessage":"Kiro toolUseEvent is empty","messagePattern":"Kiro toolUseEvent is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/kiro.js","lineNumber":829,"sourceCode":"          state.totalContentLength += content.length;\n          emitDelta(controller, { content });\n        }\n      } else if (eventType === \"reasoningContentEvent\") {\n        const value = event.payload?.reasoningContentEvent || event.payload || {};\n        const content = typeof value === \"string\" ? value : value.text || value.content || \"\";\n        if (content) {\n          state.hasReasoning = true;\n          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) {","sourceCodeStart":811,"sourceCodeEnd":847,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/kiro.js#L811-L847","documentation":"KiroExecutor processes each 'toolUseEvent' from the Kiro EventStream by normalizing its payload into an array and requiring at least one truthy entry. If the event's payload is null/undefined or an array whose first element is falsy, there is no tool-call fragment to parse, so the executor throws rather than emitting a malformed tool call downstream. This is a strict upstream-shape validation inside the streaming pipeline.","triggerScenarios":"A toolUseEvent arrives where event.payload is null, undefined, an empty array, or an array whose values[0] is null/undefined/0/'' — i.e. Kiro emitted the event frame without any tool-use data.","commonSituations":"Kiro upstream sending empty event frames during degraded/overloaded responses; protocol or API version drift changing the payload envelope (e.g. payload nested one level deeper so the unwrapped value is undefined); partially-written EventStream frames from a dropped connection.","solutions":["Retry the request — a single empty toolUseEvent mid-stream is typically a transient upstream glitch","Check whether the Kiro endpoint/API version changed its toolUseEvent payload envelope and update the executor's payload unwrapping","Inspect server logs for the surrounding event sequence (assistantResponseEvent/messageStopEvent) to confirm whether the stream was truncated","If reproducible with a specific model, switch models and report the empty toolUseEvent to the upstream provider"],"exampleFix":"// before: throw on first empty fragment\nconst values = Array.isArray(event.payload) ? event.payload : [event.payload];\nif (!values[0]) throw new Error('Kiro toolUseEvent is empty');\n// after: skip empty fragments, keep the rest of the stream\nconst values = (Array.isArray(event.payload) ? event.payload : [event.payload]).filter(Boolean);\nif (!values.length) { console.warn('[Kiro] skipping empty toolUseEvent'); return true; }","handlingStrategy":"retry","validationCode":"// sanity-check the request is dispatchable to a healthy Kiro account before sending\nif (!process.env.KIRO_TOKEN && !accounts.some(a => a.provider === 'kiro' && a.token)) {\n  throw new Error('no kiro credentials configured');\n}","typeGuard":"function hasToolUsePayload(event) {\n  const p = event?.payload;\n  return Array.isArray(p) ? p.length > 0 && Boolean(p[0]) : Boolean(p);\n}","tryCatchPattern":"try {\n  return await streamKiro(req);\n} catch (e) {\n  if (/toolUseEvent is empty/.test(e.message) && attempts < 2) return retry(req);\n  throw e;\n}","preventionTips":["Retry empty-fragment streams once before failing the client request","Pin/verify the Kiro API version after upstream updates — envelope changes surface as empty payloads","Watch upstream status for degraded Kiro responses that correlate with empty event frames","Keep stream timeouts tight so a stalled half-written EventStream fails fast and can be retried"],"tags":["kiro","tool-use","eventstream","payload-validation","streaming"],"backgroundTag":"malformed-upstream-event","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}