{"record":{"id":"0e2928745fec0d67","repo":"decolua/9router","slug":"kiro-tooluseevent-is-missing-a-tool-name","errorCode":null,"errorMessage":"Kiro toolUseEvent is missing a tool name","messagePattern":"Kiro toolUseEvent is missing a tool name","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/kiro.js","lineNumber":832,"sourceCode":"      } 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) {\n            throw new Error(\"Kiro tool name changed between fragments\");\n          }\n          appendToolInput(tool, value.input);","sourceCodeStart":814,"sourceCodeEnd":850,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/kiro.js#L814-L850","documentation":"Within a toolUseEvent fragment the executor derives the tool name and requires a non-empty string after trimming. A missing or empty 'name' makes the tool call unidentifiable — the executor cannot construct a function_call for the client — so it throws. This surfaces upstream protocol violations immediately instead of emitting an anonymous tool call.","triggerScenarios":"A toolUseEvent value where value is null/undefined, value.name is not a string, or value.name trims to an empty string — e.g. Kiro sent a fragment carrying only input deltas before the fragment containing the name, or the name field was renamed in an upstream API update.","commonSituations":"Upstream API/protocol changes renaming the 'name' field; streaming implementations that split tool metadata across frames; model emitting degenerate tool calls with blank names under heavy load or near context limits.","solutions":["Retry the request once — this is often a transient malformed fragment from the upstream","Check the installed Kiro upstream API contract for a renamed/moved name field and update the executor's field extraction","Log the full toolUseEvent payload on this path to confirm the actual shape being received","If only the input-fragment frames lack name (name sent on first frame only), make the executor carry-forward the name for known toolUseId instead of throwing"],"exampleFix":"// before: strict per-fragment name requirement\nconst name = typeof value?.name === 'string' ? value.name.trim() : '';\nif (!name) throw new Error('Kiro toolUseEvent is missing a tool name');\n// after: fall back to name already recorded for this toolUseId (continuation fragments)\nlet name = typeof value?.name === 'string' ? value.name.trim() : '';\nif (!name && value?.toolUseId) name = state.tools.get(value.toolUseId)?.name || '';\nif (!name) throw new Error('Kiro toolUseEvent is missing a tool name');","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isNamedToolFragment(value) {\n  return Boolean(value) && typeof value.name === 'string' && value.name.trim().length > 0;\n}","tryCatchPattern":"try {\n  return await streamKiro(req);\n} catch (e) {\n  if (/toolUseEvent is missing a tool name/.test(e.message)) {\n    logRawEventForDebug(e); // capture surrounding EventStream frames\n    if (attempts < 2) return retry(req);\n  }\n  throw e;\n}","preventionTips":["Retry once on tool-name validation failures before surfacing to the client","Log full toolUseEvent payloads on this path to detect upstream field renames quickly","If your upstream sends name only on the first fragment, ensure the executor carry-forwards it by toolUseId","Re-run translator/executor integration tests after any Kiro API version bump"],"tags":["kiro","tool-use","eventstream","schema-validation","streaming"],"backgroundTag":"missing-required-field","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}