The API returns 200 OK. Token usage shows input tokens consumed. The assistant message is blank. Users stare at an empty chat bubble. Dashboards show success while product displays nothing. Debugging an empty AI API response starts with structured fields in the JSON body, not assumptions about model outage.
This guide walks finish_reason and token counts, safety filter blocks, max_tokens and stop sequences, and client parsers that drop content. Engineers integrating AI API endpoints and AI image generator multimodal flows encounter the same empty-output patterns across providers.
Check finish_reason and Token Counts
Always log finish_reason, prompt_tokens, completion_tokens, and raw message object before your UI layer transforms the response. Empty visible text with completion_tokens zero means model produced no output tokens. Empty visible text with completion_tokens greater than zero means client parser or content filter stripped text after API returned payload.
| finish_reason | Typical meaning | Empty content link |
|---|---|---|
stop |
Natural end or matched stop sequence | Uncommon unless stop sequence immediate |
length |
Hit max output tokens | Rare empty; usually partial text |
content_filter |
Safety system blocked output | Common cause of blank assistant message |
tool_calls |
Model chose tools not text | Empty content field expected; check tool_calls array |
| null or missing | Stream interrupted or malformed response | Investigate network or streaming parser |
For
ai returns empty response
tickets, capture full JSON response in debug mode (redact PII for logs). Compare choices[0].message.content (OpenAI shape) or
vendor equivalent to what UI renders. If API field is null and completion_tokens is zero with content_filter, safety blocked generation.
If API field has string and UI empty, bug is downstream.
Some APIs return empty string versus null versus omitted key differently across SDK versions. Strict null checks in TypeScript may treat empty string as falsy and display placeholder incorrectly. Log typeof and length of content field explicitly.
Safety Filter Blocks
Content moderation may allow the request but block the model output, yielding HTTP 200 with empty assistant content. Input filter and output filter are separate stages. Prompt may pass while completion triggers policy on generated text, especially for medical, legal, or edge-case phrasing.
Check response for moderation metadata: content_filter_results, flagged categories, severity scores. Vendor dashboards sometimes
show blocked completions not obvious in minimal API JSON. Retry with rephrased prompt for user-facing product; do not silently retry ten times
burning tokens on identical block.
- Log category flags when present for support diagnosis without exposing to end user.
- Show user-friendly message: "Response could not be generated" with optional retry guidance.
- For enterprise, request moderation API or adjusted thresholds through vendor account team if false positives block business use cases.
- Separate system prompts that accidentally trigger policy (violence keywords in security training examples).
When investigating blank llm output debug cases, test same prompt with moderation disabled only in non-production sandbox if vendor permits. Never disable safety in production consumer apps. Compare block rate across models; newer models may have stricter default filters.
max_tokens and Stop Sequences
max_tokens set to zero or aggressive stop sequences at start of generation produce empty completions with HTTP 200.
Misconfigured env var MAX_TOKENS=0, copy-paste from example, or dynamic cap formula clamping to zero on long input causes
instant stop with no visible text.
Stop sequences matching beginning of likely output truncate before user sees characters. Example: stop list includes empty string, newline only, or common markdown token that appears first in structured output template. Review stop parameter in request logs character by character.
| Config error | Signal in logs | Fix |
|---|---|---|
| max_tokens = 0 | completion_tokens 0, finish length or stop | Set sensible minimum output budget |
| Input exceeds context | Error or truncated input in some APIs | Trim context; summarize attachments |
| Stop on first token | finish stop, tiny completion_tokens | Remove overly broad stop sequences |
Dynamic max_tokens computed as context_limit - input_tokens can go negative or zero if input miscounted (attachments processed
async, not yet in token count). Wait for file processing complete before completion request in
API
integrations with large documents.
Client Parser Dropping Content
SDKs and custom parsers often map wrong JSON path, read streaming delta only, or overwrite content with empty string on tool call branch.
Switching SDK major version changes response shape. Code reading response.text helper may return empty when model returned tool
calls only.
Streaming: accumulate delta content across chunks; some clients reset buffer on malformed SSE line and return empty final string. Non-streaming: verify you read assistant role message not system or tool message. Multimodal responses may put text in array parts while code reads legacy string field.
- Log raw HTTP body before SDK parsing in failing cases.
- Compare SDK version to vendor migration guide.
- Unit test parser against fixture JSON including tool_calls-only response.
- Check UI layer stripping markdown or HTML aggressively to empty.
- Verify charset decoding (UTF-8) on response body.
For no content ai api reports where Postman shows content but app does not, diff Postman raw response with app logged raw body. Middleware gzip or transformation rarely empties body but logging bug might truncate. Ensure error handlers do not swallow response object and return default empty string.
Decision Tree for Empty Responses
Walk this tree in order during incidents:
- HTTP status not 200: fix auth, rate limit, or server error first (out of scope for empty 200).
- completion_tokens == 0: check content_filter, max_tokens zero, immediate stop sequence.
- completion_tokens > 0 but content null: check tool_calls path, refusals field, alternate content arrays.
- Raw JSON has content string: fix client parser or UI.
- Intermittent only: compare input length, attachment readiness, streaming vs non-streaming code paths.
Refusal and Policy Fields
Some models return explicit refusal objects instead of empty content; older parsers miss them. Anthropic-style refusals and OpenAI refusals in newer APIs populate dedicated fields while content stays null. UI must read refusal message and surface policy explanation instead of blank bubble. Log refusal type for product analytics on prompt categories that trigger blocks.
Enterprise policy layers (Azure Content Safety, custom classifiers) may strip output after model generation. Response shows tokens consumed because model generated text internally before post-filter removed it. Check vendor trace or diagnostic headers when available. Compare direct API call versus routed gateway call to isolate extra filter hop.
Role and Message Type Confusion
Chat APIs return arrays of messages with roles: system, user, assistant, tool. Code iterating messages may display wrong index after multi-turn thread. Assistant message at index n may be empty because latest assistant entry is tool result placeholder. Always read last assistant message with non-null content or explicit tool_calls, not first match in array.
For image generator flows, text companion field may be optional when image URL is primary output. Product expecting caption reads text field while success is binary image payload elsewhere in JSON. Document which fields your feature treats as canonical per endpoint.
Monitoring and Alerts
Metric: rate of responses where completion_tokens zero or content length zero despite 200. Segment by model, route, and app version. Alert when empty rate exceeds baseline after deploy. Dashboard helps distinguish safety spike (vendor policy change) from parser regression (your release).
Store sample request IDs for empty responses 24 hours for support correlation. Tie to user session and prompt hash (not raw prompt if sensitive) for pattern detection.
Frequently Asked Questions
Why is content empty when finish_reason is tool_calls?
Expected behavior. Model elected to invoke tools instead of user-visible text. Your agent loop must execute tools and request follow-up completion. UI showing only message.content without tool handling looks empty. Parse tool_calls array and show "Running action..." state.
Multimodal requests return empty text but usage shows tokens. Why?
Text may live in content array parts (type: text) while code reads flat string field. Vision-only model may return
description in non-standard field. Image generator follow-up in
image
pipelines may embed metadata separately from user-facing caption.
Streaming completes but final message empty?
Client failed to merge chunks or cleared buffer on done event. Log chunk count and total merged length. Compare non-streaming same prompt; if non-streaming works, stream parser is fault. If both empty, API or config issue.
Empty responses random on same prompt?
Temperature greater than zero varies output including refusals. Safety borderline prompts flip between answer and block. Set temperature 0 for reproduction. Log seed if API supports it. Input context drift (retrieved documents changing) also causes apparent randomness.
Could caching return empty cached responses?
Yes. Reverse proxy or application cache keyed only on prompt hash may store first empty result from transient failure. Include model name and parameters in cache key or bypass cache for chat endpoints. Purge cache after parser fix deploy if empty responses persisted in CDN edge.
The Bottom Line
Empty AI API responses with HTTP 200 decode cleanly when you inspect finish_reason, token counts, safety metadata, generation parameters, and raw JSON before UI. Most production bugs are parser or config paths, not model silence. Instrument zero-completion responses and test tool-only and multimodal shapes. Teams building on AI API and image generator stacks should treat empty output as a first-class metric, not an edge case.