Skip to main content Execute set up comprehensive logging and debugging for OpenRouter. Use when investigating issues or monitoring requests. Trigger with phrases like 'openrouter debug', 'openrouter logging', 'openrouter trace', 'monitor openrouter'.
npx skills add jeremylongshore/claude-code-plugins-plus-skills --skill openrouter-debug-bundle ai automation claude-code devops mcp ai-agents
OpenRouter Debug Bundle
Current State
!node --version 2>/dev/null || echo 'N/A'
!python3 --version 2>/dev/null || echo 'N/A'
Overview
When an OpenRouter request fails or returns unexpected results, you need a structured debug bundle: the exact request, response, headers, generation metadata, and environment info. The generation ID (gen-* prefix in response.id) is the key correlator -- it lets you look up exact cost, provider used, and latency via GET /api/v1/generation?id=.
Prerequisites
An OpenRouter API key (sk-or-v1-...) exported as OPENROUTER_API_KEY — see the openrouter-install-auth skill for setup
curl and jq for the quick-debug flow and the Common Debug Checks
Python 3.8+ with the openai and requests packages for the Debug Bundle Generator
A failing or suspect request you can reproduce — its gen-* generation ID is what everything else correlates on
Instructions
Rule out environment problems first with the Common Debug Checks: verify the key via /api/v1/auth/key, confirm the model exists in /api/v1/models, and check status.openrouter.ai.
Reproduce the failure with the Quick Debug: curl command — curl -v ... | tee /tmp/openrouter-debug.txt captures request headers, response headers, and body in one transcript.
Extract the generation ID (jq -r '.id') and query GET /api/v1/generation?id=$GEN_ID to get exact cost, token counts, generation_time, and provider_name.
For failures inside an application, call debug_request() from the Python Debug Bundle Generator to capture the same request/response/error/latency/environment data as a DebugBundle and save it with bundle.save("debug_bundle.json").
Match the symptoms against the Error Handling table (missing generation ID, 502/503, model_not_found, slow TTFT).
Before sharing a bundle, redact API keys per Enterprise Considerations (sk-or-v1-... -> sk-or-v1-[REDACTED]) and include the generation ID in any OpenRouter support request.
Quick Debug: curl # Send a request and capture full response with headers
curl -v https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-H "HTTP-Referer: https://my-app.com" \
-H "X-Title: debug-test" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [{"role": "user", "content": "Say hello"}],
"max_tokens": 50
}' 2>&1 | tee /tmp/openrouter-debug.txt
# Extract generation ID from response
GEN_ID=$(jq -r '.id' /tmp/openrouter-debug.txt 2>/dev/null)
echo "Generation ID: $GEN_ID"
# Look up generation metadata (exact cost, provider, latency)
curl -s "https://openrouter.ai/api/v1/generation?id=$GEN_ID" \
-H "Authorization: Bearer $OPENROUTER_API_KEY" | jq '.data | {
model: .model,
total_cost: .total_cost,
tokens_prompt: .tokens_prompt,
tokens_completion: .tokens_completion,
generation_time: .generation_time,
provider: .provider_name
}'
Python Debug Bundle Generator import os, json, time, platform, sys
from datetime import datetime, timezone
from dataclasses import dataclass, asdict
from typing import Optional
from openai import OpenAI, APIError
import requests as http_requests
@dataclass
class DebugBundle:
timestamp: str
generation_id: Optional[str]
request_model: str
request_messages: list
request_params: dict
response_status: str
response_model: Optional[str]
response_content: Optional[str]
error_type: Optional[str]
error_message: Optional[str]
error_code: Optional[int]
latency_ms: float
generation_metadata: Optional[dict]
environment: dict
def to_json(self) -> str:
return json.dumps(asdict(self), indent=2)
def save(self, path: str = "debug_bundle.json"):
with open(path, "w") as f:
f.write(self.to_json())
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
def debug_request(
messages: list[dict],
model: str = "openai/gpt-4o-mini",
**kwargs,
) -> DebugBundle:
"""Execute a request and capture everything for debugging."""
env = {
"python": sys.version,
"platform": platform.platform(),
"openai_sdk": getattr(__import__("openai"), "__version__", "unknown"),
}
start = time.monotonic()
gen_id = None
response_model = None
content = None
error_type = None
error_msg = None
error_code = None
status = "success"
gen_meta = None
try:
response = client.chat.completions.create(
model=model, messages=messages, **kwargs
)
gen_id = response.id
response_model = response.model
content = response.choices[0].message.content
except APIError as e:
status = "error"
error_type = type(e).__name__
error_msg = str(e)
error_code = e.status_code
except Exception as e:
status = "error"
error_type = type(e).__name__
error_msg = str(e)
latency = (time.monotonic() - start) * 1000
# Fetch generation metadata if we have an ID
if gen_id:
try:
gen = http_requests.get(
f"https://openrouter.ai/api/v1/generation?id={gen_id}",
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
timeout=5,
).json()
gen_meta = gen.get("data")
except Exception:
pass
return DebugBundle(
timestamp=datetime.now(timezone.utc).isoformat(),
generation_id=gen_id,
request_model=model,
request_messages=messages,
request_params={k: v for k, v in kwargs.items() if k != "messages"},
response_status=status,
response_model=response_model,
response_content=content,
error_type=error_type,
error_message=error_msg,
error_code=error_code,
latency_ms=round(latency, 1),
generation_metadata=gen_meta,
environment=env,
)
# Usage
bundle = debug_request(
[{"role": "user", "content": "Test"}],
model="anthropic/claude-3.5-sonnet",
max_tokens=100,
)
print(bundle.to_json())
bundle.save("debug_bundle.json")
Common Debug Checks # 1. Verify API key is valid
curl -s https://openrouter.ai/api/v1/auth/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY" | jq '.data | {label, usage, limit, is_free_tier}'
# 2. Check if model exists
MODEL="anthropic/claude-3.5-sonnet"
curl -s https://openrouter.ai/api/v1/models | jq --arg m "$MODEL" '.data[] | select(.id == $m) | {id, context_length}'
# 3. Check OpenRouter status
curl -s https://status.openrouter.ai/api/v2/status.json | jq '.status'
Output Running these flows leaves you with concrete debug artifacts:
/tmp/openrouter-debug.txt — the full verbose curl transcript (request/response headers plus the completion JSON) from the quick-debug step
A generation-metadata JSON from /api/v1/generation: model, total_cost, tokens_prompt, tokens_completion, generation_time, and provider_name
debug_bundle.json — the serialized DebugBundle: timestamp, generation ID, request model/messages/params, response status and content, error type/message/code, latency_ms, generation metadata, and environment info (Python version, platform, SDK version)
One-line JSON results from the three Common Debug Checks (key label/usage/limit, model existence, OpenRouter status)
Examples Looking up a request you just sent by its generation ID:
curl -s "https://openrouter.ai/api/v1/generation?id=$GEN_ID" \
-H "Authorization: Bearer $OPENROUTER_API_KEY" | jq '.data | {model, total_cost, generation_time, provider: .provider_name}'
{
"model": "openai/gpt-4o-mini",
"total_cost": 0.000021,
"generation_time": 412,
"provider": "OpenAI"
}
If the metadata comes back empty, wait 1-2 seconds and retry with the same key that made the request. More worked examples: references/examples.md.
Error Handling Error Cause Fix No generation ID in response Request failed before reaching provider Check network, verify base URL is https://openrouter.ai/api/v1 Generation metadata missing Fetched too soon or wrong key Wait 1-2s; use same API key that made the request Intermittent 502/503 Upstream provider outage Check status.openrouter.ai; try different provider model_not_foundModel ID typo or model removed Query /api/v1/models to verify model exists Slow TTFT (>10s) Model cold start or overload Use streaming; try :floor variant for different provider
Enterprise Considerations
Always redact API keys from debug bundles before sharing (sk-or-v1-... -> sk-or-v1-[REDACTED])
Include the generation ID when contacting OpenRouter support -- it's the primary lookup key
Log debug bundles to structured storage for post-incident analysis
Set up automated debug bundle capture on 4xx/5xx responses in production
Compare failing requests against a known-good baseline to isolate changes
References
Examples | Errors
Generation API | Status
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.
CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.
CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).