OpenCode plugin that automatically switches through a hierarchy of fallback models when rate limits are hit.
opencode plugin @usful/opencode-multi-model-fallbackThis automatically:
- Installs the package from npmjs.com
- Updates your
opencode.jsoncwith the plugin entry - Sets up local scope configuration
To add to existing opencode.jsonc:
{
"plugin": ["opencode-multi-model-fallback"]
}The plugin creates its config file automatically. You can customize it at:
~/.config/opencode/rate-limit-fallback-multi.json
Default config:
{
"enabled": true,
"fallbackModels": ["openrouter/free"],
"patterns": ["rate limit", "usage limit", "too many requests", "quota exceeded", "overloaded", "capacity exceeded"],
"logging": true
}opencode plugin @usful/opencode-multi-model-fallbacknpm install @usful/opencode-multi-model-fallback
# or
bun add @usful/opencode-multi-model-fallbacknpx jsr add @usful/opencode-multi-model-fallback{
"plugin": [
"https://raw.githubusercontent.com/Aldo-f/opencode-multi-model-fallback/main/index.ts"
]
}Primary model with automatic fallback on rate limits:
{
"model": "freellm/auto"
}When freellm/auto hits a rate limit, the plugin reverts to the last user message and resubmits with openrouter/free (or configured fallback chain).
{
"fallbackModels": ["openrouter/free", "poolside/laguna-s-2.1:free", "deepseek/v4-flash-free"]
}Default location: ~/.config/opencode/rate-limit-fallback-multi.json
{
"enabled": true,
"fallbackModels": ["openrouter/free"],
"patterns": [
"rate limit",
"usage limit",
"too many requests",
"quota exceeded",
"overloaded",
"capacity exceeded"
],
"logging": true
}| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable/disable the plugin |
fallbackModels |
array | ["openrouter/free"] |
Ordered list of fallback models |
patterns |
string[] | see below | Custom rate limit detection patterns |
logging |
boolean | true |
Enable file-based logging |
fallbackOnAllErrors |
boolean | false |
Process generic session.error events as fallbacks |
maxRetries |
number | 3 |
Max number of fallback attempts per session (inclusive) |
When logging: true, structured log entries are written to:
~/.local/share/opencode/logs/rate-limit-fallback.log
Example log output:
2026-09-04T08:50:23.185Z [INFO] Rate limit hit: ? → openrouter/free {"sessionID":"ses_f94636de4ffe4i7UMTZBAcvO5m","reason":"[Poolside] poolside/laguna-s-2"}
- File log:
~/.local/share/opencode/logs/rate-limit-fallback.log - App log:
opencode app log— Warn-level entries - Toast: Error toast in the TUI for immediate feedback
Flowchart:
flowchart TD
A[User sends message to primary model] --> B[Rate limit error detected]
B --> C{If fallbackModels exhausted?}
C -- Yes --> D[Log: "All fallbacks exhausted" + Toast]
C -- No --> E[Revert to last user message]
E --> F[Send with next model in chain]
F --> G[Log: "Rate limit hit: modelA → modelB"]
G --> H{Success?}
H -- Yes --> I[Increment session index]
H -- No --> J[Reset inflight, increment retries]
I --> K[Continue with next model]
J --> L[Retry with same model]
L --> M{Retry limit reached?}
M -- Yes --> N[Log: "Max retries exceeded" + Toast]
M -- No --> O[Retry with same model]
subgraph "Configuration"
P[Config file]
Q[fallbackModels array]
R[patterns array]
S[fallbackOnAllErrors: boolean]
T[maxRetries: number]
end
P --> Q
P --> R
P --> S
P --> T
%% Session state tracking
U[SessionState: {index, queue, inflight, start, retries}]
V[Map<sessionID, SessionState>]
subgraph "Error detection"
W[session.status events]
X[status.type === "retry"]
Y[message matches patterns]
Z[session.error events]
end
W --> X --> Y --> E
Z --> E
Detailed Process:
-
Detection:
- On
session.statusevents withstatus.type === "retry"and message matching patterns - When
fallbackOnAllErrors: true, also reacts tosession.errorevents
- On
-
Fallback Logic:
- Check if
retries(state) ≥maxRetries→ stop (no more attempts) - Check if
nextIndex(state +1) ≥fallbackModels.length→ all models exhausted - Store new session state with
inflight: truewhile sending prompt
- Check if
-
Response Handling:
- On success: increment
index, store new session state withinflight: false, emit log/toast - On failure: emit error log, reset
inflight: false, incrementretriescount
- On success: increment
-
Session Reset:
- When
session.deletedevent fires, plugin removes session from tracking map → next session gets fresh state (index: -1,retries: 0) - No automatic reset on successful completion — user can manually delete session or modify config
- When
| Field | Purpose | Reset Condition |
|---|---|---|
index |
Current position in fallback chain | Reset to -1 on new session |
queue |
History of attempted indices (for debugging) | Accumulates, never cleared |
inflight |
Prevents overlapping fallback attempts | Reset to false after attempt |
start |
Timestamp when fallback began | Reset on new session |
retries |
Number of attempts for current session | Reset to 0 on new session |
- Config:
~/.config/opencode/rate-limit-fallback-multi.json - Log file:
~/.local/share/opencode/logs/rate-limit-fallback.log - Plugin registration:
opencode.jsonc(user home directory) - Plugin source:
index.tsorfile:///path/to/opencode-multi-model-fallback/index.ts
# Publish
npm run publish:github
# Install
npm install @usful/opencode-multi-model-fallback
# or
bun add @usful/opencode-multi-model-fallbackAuthentication (one-time):
echo "//npm.pkg.github.com/:_authToken=$(gh auth token)" >> ~/.npmrcnpm run publish:bun
# Install: bun add @usful/opencode-multi-model-fallbacknpm run publish:jsr
# Import: import { createPlugin } from "jsr:@usful/opencode-multi-model-fallback"See .github/workflows/publish.yml — auto-publishes on version tags.
npm version patch # 0.3.2 → 0.3.3
git push --tags origin mainMIT