ruvnet/ruflo · error
trajectory envelope not found
Error message
trajectory envelope not found: ${path} What it means
Thrown by readSealedTrajectory in @claude-flow/browser/src/application/signed-trajectory-service.ts when no file exists at the given path before reading. Paths are resolved with path.resolve (i.e. relative paths resolve against the current working directory), so the check fails both when the envelope was never written and when the process cwd differs from the one used at write time. Note the existsSync check and the readFile both target the resolved path, but a cwd change between writeSealedTrajectory and readSealedTrajectory silently redirects relative paths.
Solutions
- Pass an absolute path to both writeSealedTrajectory and readSealedTrajectory (store resolvePath(output) at write time) so cwd cannot diverge
- Verify the file exists (existsSync) and log the fully resolved path before calling readSealedTrajectory to catch path mistakes early
- If the envelope should exist, re-run the sealing step (sealTrajectory + writeSealedTrajectory) that produces it, then retry the read
- Treat a genuinely missing envelope as a cache miss: re-capture the trajectory instead of failing the whole replay pipeline
Example fix
// before
await writeSealedTrajectory(env, 'out/sealed.json'); // cwd A
await readSealedTrajectory('out/sealed.json'); // cwd B -> throws not found
// after
import { resolve } from 'node:path';
const sealedPath = resolve('out/sealed.json');
await writeSealedTrajectory(env, sealedPath);
const env2 = await readSealedTrajectory(sealedPath); Defensive patterns
Strategy: validation
Validate before calling
import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
const abs = resolve(path);
if (!existsSync(abs)) throw new Error(`no sealed envelope at ${abs}; seal it first`);
const envelope = await readSealedTrajectory(abs); Type guard
const isSignedEnvelope = (v: unknown): v is SignedTrajectoryEnvelope => typeof v === 'object' && v !== null && 'signature' in v && 'trajectory' in v;
Try / catch
try { env = await readSealedTrajectory(abs); } catch (e) { if (e instanceof Error && e.message.startsWith('trajectory envelope not found')) { env = await recaptureTrajectory(); /* re-seal + write */ } else throw e; } Prevention
- Always pass absolute paths to write/read sealed trajectories
- Persist the resolved path at write time and reuse exactly that string when reading
- Treat missing envelopes as re-captureable cache misses in replay pipelines
When it happens
Trigger: Calling readSealedTrajectory('trajectories/run-1.json') from a process whose cwd is not the directory used when writeSealedTrajectory created the file; reading an envelope id that was never sealed; a typo'd or stale path after the file was moved/deleted; a TOCTOU delete between existsSync and readFile surfaces as ENOENT instead of this message.
Common situations: Persisting sealed trajectories in one CLI invocation and replaying them in another (different cwd, so relative resolution diverges); cleanup jobs deleting old envelope files while a consumer still references them; passing a directory or an id instead of the actual file path.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- AI budget file is a symlink (refusing)
- AI job registry is a symlink (refusing)
- build evidence path is not a file or symlink
- Cannot write to project path
- case-fold repository path collision
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/0c710c4d4f1d256f.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/browser/src/application/signed-trajectory-service.ts:94
},
key,
{ sealedAt: input.sealedAt },
);
return { envelope, publicKeyHex: key.publicKeyHex };
}
/** Write a signed envelope to disk. */
export async function writeSealedTrajectory(
envelope: SignedTrajectoryEnvelope,
path: string,
): Promise<void> {
await writeFile(resolvePath(path), JSON.stringify(envelope, null, 2), 'utf8');
}
/** Read a signed envelope from disk. */
export async function readSealedTrajectory(path: string): Promise<SignedTrajectoryEnvelope> {
if (!existsSync(path)) throw new Error('trajectory envelope not found: ' + path);
const raw = await readFile(resolvePath(path), 'utf8');
return JSON.parse(raw) as SignedTrajectoryEnvelope;
}
/** Verify a sealed envelope. Thin wrapper that allows trust-list filtering. */
export function verifySealedTrajectory(
envelope: unknown,
options: { trustedPublicKeys?: string[] } = {},
): VerificationResult {
return verifyTrajectory(envelope, options);
}
/**
* Compute the replay plan from a sealed envelope + mutations.
*
* Phase 1 deliberately returns a plan rather than executing — the executor
* needs a live BrowserService and is wired in BrowserService.replayFromEnvelope.
* This keeps the signing/replay logic browser-engine-independent so it can beView on GitHub (pinned to fa13ee4ad6)