ruvnet/ruflo · error
Trajectory ${params.trajectoryId} not found
Error message
Trajectory ${params.trajectoryId} not found What it means
SONAAdapter.recordTrajectoryStep looks up params.trajectoryId in the in-memory activeTrajectories map and throws on a miss. The map is populated only by startTrajectory, and entries are removed by endTrajectory — steps are accepted only for live, started trajectories on the same adapter instance.
Source
Thrown at v3/@claude-flow/integration/src/sona-adapter.ts:273
return trajectoryId;
}
/**
* Record a step in an active trajectory
*/
async recordTrajectoryStep(params: {
trajectoryId: string;
stepId?: string;
action: string;
observation: string;
reward: number;
embedding?: number[];
}): Promise<void> {
this.ensureInitialized();
const trajectory = this.activeTrajectories.get(params.trajectoryId);
if (!trajectory) {
throw new Error(`Trajectory ${params.trajectoryId} not found`);
}
const step: SONATrajectoryStep = {
stepId: params.stepId || this.generateId('step'),
action: params.action,
observation: params.observation,
reward: params.reward,
timestamp: Date.now(),
embedding: params.embedding,
};
trajectory.steps.push(step);
trajectory.totalReward += params.reward;
this.emit('trajectory-step-recorded', {
trajectoryId: params.trajectoryId,
step
});View on GitHub (pinned to fa13ee4ad6)
Solutions
- Create the trajectory first and use exactly the id returned by startTrajectory for every subsequent step
- Stop recording after endTrajectory — the trajectory is closed by design
- Keep start/step/end on the same adapter instance and process; after a restart, start a new trajectory rather than resuming the old id
Example fix
// before
adapter.recordTrajectoryStep({ trajectoryId: 'traj-42', action, observation, reward }); // never started
// after
const trajectoryId = await adapter.startTrajectory({ /* ... */ });
adapter.recordTrajectoryStep({ trajectoryId, action, observation, reward }); Defensive patterns
Strategy: validation
Validate before calling
// Track live ids on the caller side (activeTrajectories is private to the adapter)
const live = new Set<string>();
const trajectoryId = await adapter.startTrajectory(params);
live.add(trajectoryId);
function recordStep(p: StepParams) {
if (!live.has(p.trajectoryId)) {
throw new Error(`trajectory not live: ${p.trajectoryId}`);
}
return adapter.recordTrajectoryStep(p);
} Type guard
const isLiveTrajectory = (id: string, live: Set<string>): boolean => live.has(id);
Try / catch
try {
await adapter.recordTrajectoryStep(step);
} catch (e) {
if (e instanceof Error && e.message.includes('not found')) {
// start a new trajectory (or drop the step) — never fabricate a resume of a dead id
}
throw e;
} Prevention
- Derive every step/end call from the value returned by startTrajectory — never hand-write ids
- Close trajectories in a finally block so they don't linger half-alive
- Assume in-memory trajectories die with the process; design restarts to start anew
When it happens
Trigger: Recording a step for an id that was never started, for a trajectory already ended (endTrajectory deletes it from the map), or for a trajectory started on a different adapter instance (the map is per-instance and memory-only).
Common situations: Continuing to log steps after calling endTrajectory; a process restart mid-trajectory wiping the in-memory map; splitting start/step/end across serverless invocations or worker processes.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- SSRF guard: invalid URL — ${rawUrl}
- SSRF guard: only HTTPS URLs are permitted, got ${parsed.prot
- SSRF guard: private/loopback host rejected — ${host}
- Trajectory buffer not initialized
- SONAAdapter not initialized. Call initialize() first.
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/15cdeb2b84b938d0.
Report an issue: GitHub.