decolua/9router · error
Kiro toolUseEvent is empty
Error message
Kiro toolUseEvent is empty
What it means
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.
Source
Thrown at open-sse/executors/kiro.js:829
state.totalContentLength += content.length;
emitDelta(controller, { content });
}
} else if (eventType === "reasoningContentEvent") {
const value = event.payload?.reasoningContentEvent || event.payload || {};
const content = typeof value === "string" ? value : value.text || value.content || "";
if (content) {
state.hasReasoning = true;
state.totalContentLength += content.length;
emitDelta(controller, { reasoning_content: content });
}
} else if (eventType === "codeEvent" && typeof event.payload?.content === "string") {
state.hasCode = true;
state.totalContentLength += event.payload.content.length;
emitDelta(controller, { content: event.payload.content });
} else if (eventType === "toolUseEvent") {
state.sawToolUse = true;
const values = Array.isArray(event.payload) ? event.payload : [event.payload];
if (!values[0]) throw new Error("Kiro toolUseEvent is empty");
for (const value of values) {
const name = typeof value?.name === "string" ? value.name.trim() : "";
if (!name) throw new Error("Kiro toolUseEvent is missing a tool name");
let id;
if (value.toolUseId == null) {
id = `call_${created}_${state.tools.size + 1}`;
} else if (typeof value.toolUseId !== "string" || !value.toolUseId.trim()) {
throw new Error("Kiro toolUseEvent has an invalid toolUseId");
} else {
id = value.toolUseId;
}
let tool = state.tools.get(id);
if (!tool) {
tool = { id, name };
state.tools.set(id, tool);
state.bufferedToolBytes += encoder.encode(id).byteLength + encoder.encode(name).byteLength + 32;
assertToolBufferBound();
} else if (tool.name !== name) {View on GitHub (pinned to 90b52e06ff)
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
Example fix
// before: throw on first empty fragment
const values = Array.isArray(event.payload) ? event.payload : [event.payload];
if (!values[0]) throw new Error('Kiro toolUseEvent is empty');
// after: skip empty fragments, keep the rest of the stream
const values = (Array.isArray(event.payload) ? event.payload : [event.payload]).filter(Boolean);
if (!values.length) { console.warn('[Kiro] skipping empty toolUseEvent'); return true; } Defensive patterns
Strategy: retry
Validate before calling
// sanity-check the request is dispatchable to a healthy Kiro account before sending
if (!process.env.KIRO_TOKEN && !accounts.some(a => a.provider === 'kiro' && a.token)) {
throw new Error('no kiro credentials configured');
} Type guard
function hasToolUsePayload(event) {
const p = event?.payload;
return Array.isArray(p) ? p.length > 0 && Boolean(p[0]) : Boolean(p);
} Try / catch
try {
return await streamKiro(req);
} catch (e) {
if (/toolUseEvent is empty/.test(e.message) && attempts < 2) return retry(req);
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Kiro toolUseEvent is missing a tool name
- Kiro toolUseEvent has an invalid toolUseId
- Kiro tool name changed between fragments
- Kiro tool_use stop reason did not include a complete tool ca
- AWS EventStream frame is shorter than 16 bytes
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/5a98c4db25297d35.
Report an issue: GitHub.