Skip to Content
Empty-Response Auto-Retry

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

ParameterDefaultNotes
EnabledtrueActive on every request unless overridden
Max attempts3Initial call + up to 2 retries
Validators activethinking, tool_use, output_textAll three conditions checked

Opt-Out and Tuning

Pass these headers on any request to override the defaults for that call.

HeaderTypeDefaultEffect
x-picklayer-empty-retryboolean-ish (true/false, 1/0, yes/no)trueSet to false to disable empty-response retry for this request.
x-picklayer-empty-retry-max-attemptsinteger, [1, 10]3Total attempts including the initial. 1 suppresses retry without disabling the feature.
x-picklayer-empty-retry-validatorscomma-separated listthinking,tool_use,output_textLimit 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.

HeaderTypeEmitted when
x-picklayer-empty-retry-attemptsintegerOne or more retries were attempted
x-picklayer-empty-retry-reasonstringOne or more retries were attempted

Reason values:

ValueMeaning
thinking_expected_emptyThinking/reasoning was requested but the response contained zero thinking blocks.
tool_use_expected_emptyTools were declared and stop_reason was end_turn, but the response contained no tool_use blocks and no actionable text.
output_text_emptyAll 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:

NameBehavior
NO_RETRYDisables all retry (enabled=False)
RETRY_THINKING_ONLYRetries only when thinking blocks are absent
RETRY_TOOL_USE_ONLYRetries only when tool_use blocks are absent
RETRY_DEFAULTGateway defaults (all validators, 3 attempts)

Industry Context

This follows the established pattern for gateway-level retry controls:

GatewayRetry control headerDisable pattern
PickLayerx-picklayer-empty-retrySet to false
LiteLLMx-litellm-num-retriesSet to 0
HeliconeHelicone-Retry-Enabled, Helicone-Retry-NumHelicone-Retry-Enabled: false
Portkeyx-portkey-request-timeout (gateway config)Gateway config
Last updated on