Summary
When an agent's ~/.claude/settings.json contains a model not available on the assigned subscription (e.g. claude-3-5-haiku-latest on a Claude Max subscription), task execution via chat and paid endpoints fails with a misleading "Subscription token may be expired or revoked" error. However, terminal/WebSocket sessions work fine because they pass a model=sonnet query parameter that overrides the agent's default. This creates a confusing situation where manual chat works but programmatic/paid requests always fail.
Component
Backend / Task Execution Service + Agent Runtime
Priority
P1
Error
[TaskExecService] Failed to execute task on [agent]: Task execution failed (exit code 1): Subscription token may be expired or revoked. Generate a new one with 'claude setup-token'.
The actual cause is not an expired token — it's that the model (claude-3-5-haiku-latest) is not accessible with the subscription type (Claude Max). The error message from Claude Code is misleading.
Root Cause
Two separate issues combine:
-
No model override in task execution: Terminal WebSocket connections pass model=sonnet as a URL parameter, but task_execution_service.execute_task() runs claude --print without specifying a --model flag. It relies on the agent's ~/.claude/settings.json, which may contain an incompatible model.
-
Misleading error message: When Claude Code can't use a model due to subscription restrictions, it reports "Subscription token may be expired or revoked" instead of "Model not available on your subscription." This makes debugging much harder.
Reproduction Steps
- Create an agent and assign a Claude Max subscription
- Set the agent's
~/.claude/settings.json to {"model": "claude-3-5-haiku-latest"}
- Open a terminal session with
model=sonnet → works fine
- Send a chat message via
/api/agents/{name}/chat or /api/paid/{name}/chat → fails with "token expired" error
- The same agent, same token, same container — different result depending on the code path
Suggested Fix
Option A: Pass model to task execution (recommended)
The task execution service should accept and pass a --model parameter to claude --print. This could come from:
- The chat/paid request body (already has
model field in some endpoints)
- The agent's DB config (a
default_model column)
- A fallback default like
sonnet
# In task_execution_service.py, when building the claude command:
cmd = ["claude", "--print", message]
if model:
cmd.extend(["--model", model])
Option B: Validate model compatibility on subscription assignment
When assigning a subscription, check if the agent's settings.json model is compatible. If not, warn or auto-update to a compatible default.
Option C: Improve error message (separate issue)
Claude Code should distinguish between "token expired" and "model not available on subscription" to make debugging easier.
Environment
Related
Summary
When an agent's
~/.claude/settings.jsoncontains a model not available on the assigned subscription (e.g.claude-3-5-haiku-lateston a Claude Max subscription), task execution via chat and paid endpoints fails with a misleading "Subscription token may be expired or revoked" error. However, terminal/WebSocket sessions work fine because they pass amodel=sonnetquery parameter that overrides the agent's default. This creates a confusing situation where manual chat works but programmatic/paid requests always fail.Component
Backend / Task Execution Service + Agent Runtime
Priority
P1
Error
The actual cause is not an expired token — it's that the model (
claude-3-5-haiku-latest) is not accessible with the subscription type (Claude Max). The error message from Claude Code is misleading.Root Cause
Two separate issues combine:
No model override in task execution: Terminal WebSocket connections pass
model=sonnetas a URL parameter, buttask_execution_service.execute_task()runsclaude --printwithout specifying a--modelflag. It relies on the agent's~/.claude/settings.json, which may contain an incompatible model.Misleading error message: When Claude Code can't use a model due to subscription restrictions, it reports "Subscription token may be expired or revoked" instead of "Model not available on your subscription." This makes debugging much harder.
Reproduction Steps
~/.claude/settings.jsonto{"model": "claude-3-5-haiku-latest"}model=sonnet→ works fine/api/agents/{name}/chator/api/paid/{name}/chat→ fails with "token expired" errorSuggested Fix
Option A: Pass model to task execution (recommended)
The task execution service should accept and pass a
--modelparameter toclaude --print. This could come from:modelfield in some endpoints)default_modelcolumn)sonnetOption B: Validate model compatibility on subscription assignment
When assigning a subscription, check if the agent's
settings.jsonmodel is compatible. If not, warn or auto-update to a compatible default.Option C: Improve error message (separate issue)
Claude Code should distinguish between "token expired" and "model not available on subscription" to make debugging easier.
Environment
f1dfd7dRelated
src/backend/services/task_execution_service.py— executes tasks without model overridesrc/backend/routers/paid.py— paid endpoint (also affected)src/backend/routers/chat.py— chat endpoint (also affected)