Routing Configuration
Configure how requests are routed to different models. The same Router
rules apply after Unified normalization for every chat inbound protocol
(Messages, Chat Completions, Responses). FIM uses Router.fim on its own
pipeline — see FIM.
Default Routing
Set the default model for all requests:
{
"Router": {
"default": "deepseek,deepseek-chat"
}
}
Built-in Scenarios
Background Tasks
Route background tasks to a lightweight model:
{
"Router": {
"background": "groq,llama-3.3-70b-versatile"
}
}
Thinking Mode (Plan Mode)
Route thinking-intensive tasks to a more capable model:
{
"Router": {
"think": "deepseek,deepseek-chat"
}
}
Long Context
Route requests with long context:
{
"Router": {
"longContextThreshold": 100000,
"longContext": "gemini,gemini-1.5-pro"
}
}
Web Search
Route web search tasks:
{
"Router": {
"webSearch": "deepseek,deepseek-chat"
}
}
FIM (fill-in-the-middle)
Route POST /v1/fim/completions to a FIM-capable provider (must use a fim.* transformer).
Codestral:
{
"Router": {
"fim": "codestral-fim,codestral-latest"
}
}
Local Qwen (LM Studio):
{
"Router": {
"fim": "lmstudio-qwen-fim,qwen/qwen2.5-coder-14b"
}
}
Bare models on the FIM endpoint resolve via Router.fim, then Router.default. Provider JSON and client setup for both cases: FIM Completions API.
Image Tasks
Route image-related tasks:
{
"Router": {
"image": "gemini,gemini-1.5-pro"
}
}
Fallback
When a request fails, you can configure a list of backup models. The system will try each model in sequence until one succeeds:
Basic Configuration
{
"Router": {
"default": "deepseek,deepseek-chat",
"background": "ollama,qwen2.5-coder:latest",
"think": "deepseek,deepseek-reasoner",
"longContext": "openrouter,google/gemini-2.5-pro-preview",
"longContextThreshold": 60000,
"webSearch": "gemini,gemini-2.5-flash"
},
"fallback": {
"default": [
"aihubmix,Z/glm-4.5",
"openrouter,anthropic/claude-sonnet-4"
],
"background": [
"ollama,qwen2.5-coder:latest"
],
"think": [
"openrouter,anthropic/claude-3.7-sonnet:thinking"
],
"longContext": [
"modelscope,Qwen/Qwen3-Coder-480B-A35B-Instruct"
],
"webSearch": [
"openrouter,anthropic/claude-sonnet-4"
],
"fim": [
"codestral-fim,codestral-latest"
],
"subagent": [
"openrouter,anthropic/claude-sonnet-4"
]
}
}
How It Works
- Trigger: When a model request fails for a routing scenario. Eligible failures include provider HTTP error responses and provider network/transport errors (for example connection reset or fetch failures). Client disconnects / aborts do not trigger fallback.
- Backoff: Before the first fallback attempt (and between later attempts), CCR waits using the upstream
Retry-Afterheader when present; otherwise it uses exponential backoff. - Auto-switch: The system checks the fallback configuration for that scenario.
- Sequential retry: Tries each backup model in order. If the client disconnects during a wait or attempt, remaining fallbacks are cancelled.
- Success: Once a model responds successfully, returns immediately.
- All failed: If all backup models fail, returns the original error.
Configuration Details
- Format: Each backup model format is
provider,model - Validation: Backup models must exist in the
Providersconfiguration - Flexibility: Different scenarios can have different fallback lists
- Optional: If a scenario doesn't need fallback, omit it or use an empty array
- Subagents:
fallback.subagentoverrides fallback for subagent-routed requests. If omitted, subagents inheritfallback.defaultfor backward compatibility. - Abort-aware: Closing the client mid-request cancels fallback waits and further attempts
Request-Scoped Errors
By default CCR decides fallback eligibility from the HTTP status alone
(408/409/425/429/5xx + network errors). request_scoped_errors refines that
with body-pattern rules — per provider, plus a top-level global list.
Provider rules are evaluated first; the first match wins.
{
"request_scoped_errors": [
{ "status": 400, "match": ["maximum_context_length"], "action": "stop" },
{ "status": 400, "match": ["server_is_overloaded"], "action": "continue" }
],
"Providers": [
{
"name": "codex",
"api_base_url": "https://chatgpt.com/backend-api/codex",
"api_key": "oauth_dummy_key",
"models": ["gpt-5"],
"transformer": { "use": ["openai-responses", "codex"] },
"request_scoped_errors": [
{ "status": 429, "match": ["usage_limit_reached"], "action": "continue-and-cooldown" }
]
}
]
}
- Match:
status(optional) must equal the error status;matchsubstrings (case-insensitive) andmatch_regexpatterns hit against the raw upstream error text (first 16,384 characters) plus a specific error code. When raw text is available, it replaces CCR's sanitized message andError from provider(...)prefix for matching. Otherwise the error message and captured upstream body are used. CCR's genericapi_errortype and catch-allprovider_response_errorcode are not added to matching text. A rule with no body patterns matches on status alone. Raw text is retained for matching only; clients and logs still see sanitized error details. - Actions:
stop(terminal — no fallback),continue(force fallback even when the status alone would not qualify), plusstop-and-cooldown/continue-and-cooldown, which additionally cool theprovider,modeldown (default 60 s, overridable per rule withcooldown_seconds, minimum 1). The cooldown applies to fallback candidates only: the fallback loop skips a cooled-down model and spends no Retry-After/backoff wait on it, while a model routed as the primary is still attempted. A trailing[1m]marker does not change the cooled-down identity. - Validation: a rule with an unknown key (for example
matches), an invalidaction, a non-integerstatus, a pattern list that is not an array of non-empty strings, an invalid regex or acooldown_secondsbelow 1 is ignored, and CCR logs a warning naming the rule index and the reason. - Key spellings:
request_scoped_errors/requestScopedErrors/request-scoped-errorswork on both providers and the top level; provider updates through any spelling replace the stored canonical list.match_regex/matchRegex/match-regexandcooldown_seconds/cooldownSecondsare equivalent.
Use Cases
Scenario 1: Primary Model Quota Exhausted
{
"Router": {
"default": "openrouter,anthropic/claude-sonnet-4"
},
"fallback": {
"default": [
"deepseek,deepseek-chat",
"aihubmix,Z/glm-4.5"
]
}
}
Automatically switches to backup models when the primary model quota is exhausted.
Scenario 2: Service Reliability
{
"Router": {
"background": "volcengine,deepseek-v3-250324"
},
"fallback": {
"background": [
"modelscope,Qwen/Qwen3-Coder-480B-A35B-Instruct",
"dashscope,qwen3-coder-plus"
]
}
}
Automatically switches to other providers when the primary service fails.
Log Monitoring
The system logs detailed fallback process:
[warn] Request failed for default, trying 2 fallback models
[info] Waiting 2000ms before first fallback attempt
[info] Trying fallback model: aihubmix,Z/glm-4.5
[warn] Fallback model aihubmix,Z/glm-4.5 failed: API rate limit exceeded
[info] Waiting 4000ms before next fallback attempt
[info] Trying fallback model: openrouter,anthropic/claude-sonnet-4
[info] Fallback model openrouter,anthropic/claude-sonnet-4 succeeded
Upstream failure details in logs and client error payloads are privacy-sanitized (hosts, IPs, bearer tokens, and API key material are redacted).
Important Notes
- Cost consideration: Backup models may incur different costs, configure appropriately
- Performance differences: Different models may have varying response speeds and quality
- Quota management: Ensure backup models have sufficient quotas
- Testing: Regularly test the availability of backup models
- Retry-After: When providers return
Retry-After, fallback waits honor that delay before trying the next model
Project-Level Routing
Configure routing per project in ~/.claude/projects/<project-id>/claude-code-router.json:
{
"Router": {
"default": "groq,llama-3.3-70b-versatile"
}
}
Project-level configuration takes precedence over global configuration.
Custom Router
Create a custom JavaScript router function:
- Create a router file (e.g.,
custom-router.js):
module.exports = function(config, context) {
// Analyze the request context
const { scenario, projectId, tokenCount } = context;
// Custom routing logic
if (scenario === 'background') {
return 'groq,llama-3.3-70b-versatile';
}
if (tokenCount > 100000) {
return 'gemini,gemini-1.5-pro';
}
// Default
return 'deepseek,deepseek-chat';
};
- Set the
CUSTOM_ROUTER_PATHenvironment variable:
export CUSTOM_ROUTER_PATH="/path/to/custom-router.js"
Token Counting
The router uses tiktoken (cl100k_base) to estimate request token count. This is used for:
- Determining if a request exceeds
longContextThreshold - Custom routing logic based on token count
Subagent Routing
Claude Code subagent requests can be routed in three ways (highest priority first):
- Explicit tag in system or message text (the tag is stripped before the upstream request):
<CCR-SUBAGENT-MODEL>provider,model</CCR-SUBAGENT-MODEL>
Please help me analyze this code...
provider/model is also accepted and normalized to provider,model.
- Environment variable for Claude Code subagent turns (when Claude Code marks the request as a subagent, for example via its billing helper header):
export CLAUDE_CODE_SUBAGENT_MODEL="provider,model"
- If neither tag nor env model is set, normal Router rules apply.
Next Steps
- Transformers - Apply transformations to requests
- Custom Router - Advanced custom routing