Empty-Response Auto-Retry
PickLayer auto-retries upstream calls that return shape-violating empty responses — zero thinking blocks when thinking was requested, zero tool_use when tools were declared, all-empty content — so callers don’t have to.
This is transparent by default and applies uniformly to all three AI ingresses: /v1/messages, /v1/responses, and /v1/chat/completions.
Default Behavior
| Parameter | Default | Notes |
|---|---|---|
| Enabled | true | Active on every request unless overridden |
| Max attempts | 3 | Initial call + up to 2 retries |
| Validators active | thinking, tool_use, output_text | All three conditions checked |
Opt-Out and Tuning
Pass these headers on any request to override the defaults for that call.
| Header | Type | Default | Effect |
|---|---|---|---|
x-picklayer-empty-retry | boolean-ish (true/false, 1/0, yes/no) | true | Set to false to disable empty-response retry for this request. |
x-picklayer-empty-retry-max-attempts | integer, [1, 10] | 3 | Total attempts including the initial. 1 suppresses retry without disabling the feature. |
x-picklayer-empty-retry-validators | comma-separated list | thinking,tool_use,output_text | Limit which conditions trigger retry. Accepted: thinking, tool_use, output_text. |
All three headers apply identically across all three ingress protocols.
Response Telemetry
When retries occurred, PickLayer adds diagnostic headers to the response.
| Header | Type | Emitted when |
|---|---|---|
x-picklayer-empty-retry-attempts | integer | One or more retries were attempted |
x-picklayer-empty-retry-reason | string | One or more retries were attempted |
Reason values:
| Value | Meaning |
|---|---|
thinking_expected_empty | Thinking/reasoning was requested but the response contained zero thinking blocks. |
tool_use_expected_empty | Tools were declared and stop_reason was end_turn, but the response contained no tool_use blocks and no actionable text. |
output_text_empty | All content blocks were empty/whitespace despite non-zero billed output tokens. |
Billing Note
Retry attempts are real upstream calls — providers (Bedrock, Anthropic, OpenAI, etc.) bill PickLayer for every successful 200 OK they return, regardless of whether the gateway validator accepted the content. PickLayer’s billing model is pass-through with markup: every billed upstream call is billed to you. A request that retries twice and recovers on the third attempt is billed as 3 calls (with PickLayer’s standard markup), not 1.
This is why the response telemetry headers x-picklayer-empty-retry-attempts and x-picklayer-empty-retry-reason matter — they let you audit exactly why a single logical request produced a multi-call charge. To eliminate the cost amplification entirely on a per-request basis, send x-picklayer-empty-retry: false (raw upstream flake passes through unmodified) or x-picklayer-empty-retry-max-attempts: 1 (equivalent in effect).
Disable Retry for One Call
curl https://api.picklayer.ai/api/org/acme/app/myapp/v1/messages \
-H "Authorization: Bearer $PL_JWT" \
-H "Content-Type: application/json" \
-H "x-picklayer-empty-retry: false" \
-d '{
"model": "anthropic/claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello"}]
}'Python SDK Helper
The picklayer Python SDK exposes a typed EmptyRetryConfig class:
from picklayer import PickLayerClient
from picklayer.empty_retry import EmptyRetryConfig, NO_RETRY
client = PickLayerClient(org_handler="acme", app_handler="myapp", token="...")
# Disable retry for a specific call
resp = client.messages({...}, extra_headers=NO_RETRY.as_headers())
# Limit to 2 attempts, only watch for empty tool_use
cfg = EmptyRetryConfig(max_attempts=2, validators={"tool_use"})
resp = client.messages({...}, extra_headers=cfg.as_headers())
# Use as default_headers when routing the Anthropic SDK through PickLayer
import anthropic
cfg = EmptyRetryConfig(max_attempts=5, validators={"thinking", "tool_use"})
ac = anthropic.Anthropic(
base_url="https://api.picklayer.ai/api/org/acme/app/myapp/v1",
api_key=PL_JWT,
default_headers=cfg.as_headers(),
)Convenience singletons exported by picklayer.empty_retry:
| Name | Behavior |
|---|---|
NO_RETRY | Disables all retry (enabled=False) |
RETRY_THINKING_ONLY | Retries only when thinking blocks are absent |
RETRY_TOOL_USE_ONLY | Retries only when tool_use blocks are absent |
RETRY_DEFAULT | Gateway defaults (all validators, 3 attempts) |
Industry Context
This follows the established pattern for gateway-level retry controls:
| Gateway | Retry control header | Disable pattern |
|---|---|---|
| PickLayer | x-picklayer-empty-retry | Set to false |
| LiteLLM | x-litellm-num-retries | Set to 0 |
| Helicone | Helicone-Retry-Enabled, Helicone-Retry-Num | Helicone-Retry-Enabled: false |
| Portkey | x-portkey-request-timeout (gateway config) | Gateway config |