Last reviewed: 2026-09-03
Direct answer
Reliable CometAPI truncated response detection should be a control-flow gate inside the coding-agent loop. After every model call, capture the terminal completion signal, normalize it into a provider-neutral outcome, and keep all side effects locked until the result is both a completion candidate and valid for the task.
Do not infer completion from an HTTP success status, balanced braces, a closing code fence, or prose that sounds final. The CometAPI Chat Completions documentation
defines finish_reason values with different meanings: stop indicates a natural stop or configured stop sequence, length indicates that the output limit was reached, tool_calls indicates a requested tool action, and content_filter indicates a policy stop. It also warns that response fields can vary among routed providers.
That gives an OpenAI-compatible client four distinct branches. Treat stop as a candidate for validation, length as truncated, tool_calls as action required, and content_filter as a policy outcome. A missing or unknown terminal value is indeterminate and should fail closed.
Native provider surfaces use different names. The OpenAI Chat Completions reference
defines length as reaching the request’s maximum generated-token limit. The Gemini GenerateContent reference
uses MAX_TOKENS. The Anthropic SDK Message type
identifies both max_tokens and model_context_window_exceeded as incomplete outcomes. Normalize those signals before the agent decides whether to parse output, call a tool, apply a patch, update memory, or create a commit.
Who this is for
This guide is for developers and platform engineers who run autonomous or supervised coding agents through CometAPI, especially when one agent loop can switch among model families. It is relevant when responses contain patches, structured reports, tool arguments, test plans, or other artifacts that downstream code may consume automatically.
The gate matters for both buffered and streamed calls. Stream consumers should pair it with the site’s stream parser guardrails , because a connection ending is not itself proof that the provider emitted a terminal completion event.
Key takeaways
- Read terminal metadata instead of guessing from text shape.
- Normalize provider-specific signals into
candidate_complete,truncated,action_required,policy_stop, orindeterminate. - Keep tools, patch application, commits, deployments, and durable memory writes locked until validation passes.
- Treat a clean generation stop and a valid task result as separate checks.
- Log operational metadata, not prompts, response bodies, repository contents, request headers, or tool arguments.
- Test every branch with fixtures before enabling model fallback or automatic continuation.
A concrete happy/error-path workflow
- Before sending the request, assign a short run identifier, record the selected model and endpoint family, establish an output budget, and set the run state to
effects_locked. - Collect the response without exposing partial text or partial tool arguments to an executor. For streaming calls, accumulate chunks in a staging buffer.
- Require successful transport and terminal metadata. CometAPI’s streaming example shows intermediate chunks with a null
finish_reason, a final chunk with a terminal value, and a closingDONEmarker. A conservative client should require the terminal value and clean stream termination; if either is missing, classify the result as indeterminate. - Pass the raw signal through one normalization function. Keep provider vocabulary out of the rest of the agent controller.
- On the happy path, a
candidate_completeresult proceeds to task-level validation. Validate the expected schema, patch, file manifest, or tool-call envelope. Unlock the next action only after those checks pass. - On the error path, a
truncatedorindeterminateresult remains quarantined. Do not execute tools, apply a partial diff, add the text to durable memory, or present it to another agent as an authoritative completed turn. Record the decision, then choose a bounded continuation, a fresh request with a larger budget, or human escalation. - Handle
action_requiredandpolicy_stopseparately. A tool request is not a finished answer, while a filtered or refused result should not be retried merely by increasing the output limit.
A small normalization boundary can look like this:
TRUNCATED = {
"openai_compatible": {"length"},
"gemini_native": {"MAX_TOKENS"},
"anthropic_native": {"max_tokens", "model_context_window_exceeded"},
}
CANDIDATE_COMPLETE = {
"openai_compatible": {"stop"},
"gemini_native": {"STOP"},
"anthropic_native": {"end_turn"},
}
ACTION_REQUIRED = {
"openai_compatible": {"tool_calls"},
"anthropic_native": {"tool_use", "pause_turn"},
}
POLICY_STOP = {
"openai_compatible": {"content_filter"},
"gemini_native": {"SAFETY"},
"anthropic_native": {"refusal"},
}
def classify(provider, raw_signal, terminal_metadata_seen=True):
if not terminal_metadata_seen or raw_signal is None:
return "indeterminate"
if raw_signal in TRUNCATED.get(provider, set()):
return "truncated"
if raw_signal in ACTION_REQUIRED.get(provider, set()):
return "action_required"
if raw_signal in POLICY_STOP.get(provider, set()):
return "policy_stop"
if raw_signal in CANDIDATE_COMPLETE.get(provider, set()):
return "candidate_complete"
return "indeterminate"
This intentionally uses a deny-by-default final branch. New provider values cannot silently become successful results.
A sanitized decision log can retain enough evidence for triage without copying generated code or sensitive request material:
{
"timestamp": "2026-09-03T00:00:00Z",
"run_id": "run-42",
"request_id": "req-42",
"model_id": "selected-model",
"provider_family": "openai_compatible",
"endpoint_family": "chat_completions",
"http_status": 200,
"stream_terminal_seen": true,
"raw_finish_signal": "length",
"normalized_outcome": "truncated",
"input_tokens": 4200,
"output_tokens": 800,
"response_bytes": 6120,
"tool_calls_present": false,
"schema_valid": false,
"retry_count": 0,
"decision": "block_side_effects"
}
Keep retention and access controls consistent with the rest of your agent telemetry. If detailed content is needed for a specific investigation, place it in an already approved evidence store rather than expanding routine logs.
Sources checked
The following public sources were refetched successfully on 2026-09-03:
- CometAPI Chat Completions documentation
supports the gateway response shape, streaming sequence, output-limit fields, and documented
finish_reasonmeanings. - OpenAI Chat Completions API reference
supports the OpenAI
lengthclassification and the distinction between output limits and natural stopping. - Google Gemini GenerateContent reference
supports the Gemini
FinishReasonvocabulary, includingSTOP,MAX_TOKENS, and separate safety-related outcomes. - Anthropic Python SDK Message type
supports Claude’s
stop_reasonvalues, includingend_turn,max_tokens,tool_use,pause_turn,refusal, andmodel_context_window_exceeded.
Contract details to verify
Your adapter should preserve the raw provider value and emit one normalized outcome. Do not discard the raw field; it is useful when a provider adds a value that the current client does not recognize.
| Surface | Raw terminal value | Normalized outcome | Operator action |
|---|---|---|---|
| OpenAI-compatible Chat Completions | stop | candidate_complete | Validate the task artifact |
| OpenAI-compatible Chat Completions | length | truncated | Keep effects locked and replan |
| OpenAI-compatible Chat Completions | tool_calls | action_required | Validate and authorize the tool request |
| OpenAI-compatible Chat Completions | content_filter | policy_stop | Preserve the classification and follow policy handling |
| Gemini native | STOP | candidate_complete | Validate the task artifact |
| Gemini native | MAX_TOKENS | truncated | Keep effects locked and replan |
| Anthropic native | end_turn | candidate_complete | Validate the task artifact |
| Anthropic native | max_tokens | truncated | Keep effects locked and replan |
| Anthropic native | model_context_window_exceeded | truncated | Reduce context or split the task |
| Anthropic native | tool_use or pause_turn | action_required | Follow the corresponding controlled continuation path |
A candidate-complete signal is necessary but not sufficient. CometAPI documents that stop can also mean a configured stop sequence was reached. If your client sets stop sequences, verify the actual contract that sequence represents. A premature delimiter must not unlock an executor merely because the transport calls it a stop.
Check output budgets at the same boundary. CometAPI documents max_completion_tokens as including visible output and reasoning tokens for applicable models. Consequently, a response can hit its limit with less visible text than an operator expected. When the terminal signal is length, balanced JSON or a plausible-looking patch does not override that evidence.
For structured output, validate all required fields and an explicit completion state where the workflow needs one. For patches, parse the diff, check that every intended file is represented, confirm paths remain in scope, and perform a dry-run application before changing the worktree. For tool calls, require a complete tool name and argument object, then apply the normal permission policy. For prose reports, verify the expected sections or manifest rather than relying on a final-sounding sentence.
Streaming adds another contract. Track whether terminal metadata was seen, whether the documented closing marker arrived, whether the connection reported an error, and whether exactly one final decision was produced. A socket close before the terminal chunk should become indeterminate, not candidate_complete.
Failure modes
Treating HTTP success as task success. The server can return a valid response object whose generation stopped at an output limit. Branch on terminal metadata before looking at the prose.
Accepting a syntactically valid prefix. A truncated JSON object can omit optional work, and a partial patch can end at a line boundary. Schema, scope, and task-level checks must follow the completion check.
Publishing streamed text before the final event. Intermediate chunks can have no terminal reason. Keep them in a staging buffer until the final metadata and clean terminator arrive.
Collapsing provider vocabulary too early. If an adapter converts every non-error response to done, fallback from one model family to another can erase the difference between length, MAX_TOKENS, and max_tokens. Preserve both raw and normalized values.
Executing a tool request as though the answer ended. tool_calls and tool_use represent a transition in the agent protocol, not completed task output. Validate permissions and arguments before proceeding.
Blindly retrying a side-effecting turn. A retry can repeat work if any previous tool call escaped quarantine. Keep an execution ledger and use the site’s tool-call retry safeguards before automating recovery.
Increasing the budget for policy outcomes. A filtered, refused, or safety-stopped response is not ordinary token truncation. Route it through the relevant policy path instead of a length retry.
Logging the entire failed payload by default. Partial output may contain repository code, issue text, or tool arguments. Routine logs should hold identifiers, counts, terminal signals, validation results, and decisions. Use the coding-agent telemetry review guide to keep those records useful and bounded.
FAQ
Is finish_reason: stop enough to let the agent continue?
No. It establishes a candidate-complete generation on the OpenAI-compatible path, but CometAPI documents that stop can also represent a configured stop sequence. Validate the task artifact before unlocking tools or writes.
Does strict JSON output eliminate truncation checks?
No. Structured validation and completion detection answer different questions. First establish that the provider did not report truncation or an indeterminate stream. Then validate the returned object against the expected schema and business rules.
Should the client automatically continue every truncated response?
Not by default. Continuation is safest when the output format supports deterministic stitching, no side effect has run, the remaining task is clear, and the retry budget is bounded. Otherwise, issue a fresh request with revised context or escalate for review.
What if the terminal signal is missing?
Classify the response as indeterminate. Preserve sanitized metadata, keep effects locked, and investigate the transport or adapter. Do not reinterpret missing evidence as a natural stop.
How should stop sequences be handled?
Give each configured stop sequence an application meaning. If it marks a complete envelope, verify that envelope. If it is only a formatting delimiter, it must not serve as proof that the underlying coding task finished.
What should happen to partial output?
Keep it quarantined for bounded diagnosis or an approved continuation strategy. Do not apply it to the worktree, execute embedded actions, write it to durable agent memory, or pass it to another agent as a completed result.
Reader next step
Implement one normalization function and put it immediately before every executor, patch applier, commit step, and durable-memory write. Create fixtures for stop, length, tool_calls, content_filter, STOP, MAX_TOKENS, end_turn, max_tokens, model_context_window_exceeded, a missing signal, and an unknown future value. Run those fixtures against buffered and streamed adapters, and assert that only a validated candidate_complete result can unlock side effects.
Then inspect a small sample of sanitized production decisions. Confirm that raw signals survive normalization, truncated outputs never reach executors, and retries remain bounded. If you are ready to place that boundary behind a multi-model gateway, Start with CometAPI and enable tool execution only after the detector passes its fixture suite.