Installs into .claude/skills of the current project.
Are you the author of Openrouter Debug Bundle?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-openrouter-debug-bundle-5fde2e2d)
---
name: openrouter-debug-bundle
description: 'Create debug bundles for troubleshooting OpenRouter API issues. Use
when diagnosing failures, unexpected responses, or latency problems. Triggers: ''openrouter
debug'', ''openrouter troubleshoot'', ''debug openrouter request'', ''openrouter
issue''.
'
allowed-tools: Read, Write, Edit, Grep, Bash(curl:*), Bash(jq:*), Bash(python3:*), Bash(node:*)
version: 1.20.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- openrouter
- debugging
- troubleshooting
compatibility: Designed for Claude Code
---
# 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
1. 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`.
2. 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.
3. 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`.
4. 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")`.
5. Match the symptoms against the Error Handling table (missing generation ID, 502/503, `model_not_found`, slow TTFT).
6. 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
```bash
# 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
```python
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
```bash
# 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:
```bash
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}'
```
```json
{
"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_found` | Model 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](https://status.openrouter.ai)