ruvnet/ruflo · error · Error
User token not found
Error message
User token not found
What it means
In task_assign's simple implementation, after the task is found and the already-assigned soft-return is skipped, the handler refuses to assign tasks whose status is terminal: 'completed', 'cancelled', or 'failed' all throw 'Cannot assign task with status: <status>'. This is a lifecycle guard — dead tasks cannot be handed to an agent. Note the asymmetry: 'pending', 'queued', 'assigned', and 'running' are all assignable, and 'failed' is terminal here but still updatable in task_update.
Solutions
- Check task_status immediately before assigning and only proceed for pending/queued/assigned/running
- If the task failed and needs rework, create a new task carrying over metadata instead of resurrecting the terminal one
- Catch this error specifically and treat it as a signal to refresh your local task view
- Avoid fire-and-forget assignment races: mark intent in your orchestrator before calling task_assign
Example fix
// before
await client.callTool('task_assign', { taskId, agentId: 'worker-2' }); // task.status === 'failed' -> throws [1136]
// after
const { status } = await client.callTool('task_status', { taskId });
if (!['pending', 'queued', 'assigned', 'running'].includes(status)) {
const { task } = await client.callTool('task_create', { type: task.type, description: `retry of ${taskId}` });
await client.callTool('task_assign', { taskId: task.id, agentId: 'worker-2' });
} else {
await client.callTool('task_assign', { taskId, agentId: 'worker-2', reassign: true });
} Defensive patterns
Strategy: type-guard
Validate before calling
const ASSIGNABLE = new Set(['pending', 'queued', 'assigned', 'running']);
const { status } = await client.callTool('task_status', { taskId });
if (!ASSIGNABLE.has(status)) throw new Error(`task ${taskId} is ${status}; create a new task instead`); Type guard
type TaskStatus = 'pending' | 'queued' | 'assigned' | 'running' | 'completed' | 'failed' | 'cancelled';
function isAssignableStatus(s: TaskStatus): s is 'pending' | 'queued' | 'assigned' | 'running' {
return s === 'pending' || s === 'queued' || s === 'assigned' || s === 'running';
} Try / catch
try {
await client.callTool('task_assign', { taskId, agentId });
} catch (e) {
if (e instanceof Error && e.message.startsWith('Cannot assign task with status')) {
// terminal task — create a successor task instead of retrying
} else throw e;
} Prevention
- Narrow status with isAssignableStatus() before every assign call
- For failed-task retries, create a new task carrying the old metadata
- Re-check status right before dispatch in concurrent worker pools
When it happens
Trigger: Assigning a task that a worker already completed between your status check and the assign call; retrying assignment of a failed task without resetting it; re-dispatching cancelled tasks during a re-run; racing a second agent against a task that just finished.
Common situations: Retry logic that blindly re-assigns after timeouts when the task actually failed; UIs that show stale 'pending' state and let users assign; pipelines that do not branch on terminal status before dispatch.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Unauthorized
- Failed to create share link
- Connection pool is shutting down
- HTTP transport already running
- JSON.stringify(response.error)
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/7f073d76a0937f54.
Report an issue: GitHub.
Appendix: source
Thrown at ruflo/src/ruvocal/src/lib/server/apiToken.ts:6
import { config } from "$lib/server/config";
export function getApiToken(locals: App.Locals | undefined) {
if (config.USE_USER_TOKEN === "true") {
if (!locals?.token) {
throw new Error("User token not found");
}
return locals.token;
}
return config.OPENAI_API_KEY || config.HF_TOKEN;
}
View on GitHub (pinned to fa13ee4ad6)