ruvnet/ruflo · error · Error

trajectory envelope not found: ${path}

Error message

trajectory envelope not found: ${path}

What it means

Thrown synchronously by readSealedTrajectory(path) when existsSync(path) reports the file is absent. The function then would readFile + JSON.parse the envelope, but it guards the missing-file case first with this error. Note it uses existsSync (synchronous) then readFile (async) — a TOCTOU gap means the file could disappear between the two calls, surfacing as a different ENOENT.

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 be

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Confirm the path was produced by a successful writeSealedTrajectory call in the same process.
  2. Use an absolute path (resolvePath is applied internally, but absolute removes cwd ambiguity).
  3. Guard the call with existsSync yourself only when you also control concurrent deletion; otherwise wrap in try/catch and treat absence as 'no trajectory yet'.
  4. If reading across processes/containers, verify the shared volume mount and that the writer flushed before the reader starts.

Example fix

// before
const env = await readSealedTrajectory(`./runs/${id}`); // throws 61

// after
import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
const p = resolve('./runs', `${id}.json`);
const env = existsSync(p)
  ? await readSealedTrajectory(p)
  : undefined;
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
function assertTrajectoryReadable(p) {
  const abs = resolve(p);
  if (!existsSync(abs)) throw new Error(`no trajectory at ${abs}`);
  return abs;
}

Type guard

null

Try / catch

try {
  const env = await readSealedTrajectory(path);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('trajectory envelope not found')) {
    return undefined; // treat absence as 'no trajectory yet'
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling readSealedTrajectory with a path that was never written by writeSealedTrajectory, a relative path resolved against an unexpected cwd, a path on a different filesystem/container, or a path whose write failed silently upstream.

Common situations: Passing a trajectoryId-derived path before sealing; using a relative path that resolves differently in tests vs. runtime; reading from a temp dir cleared between CI steps; mistyping the extension (.json vs none).

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/0c710c4d4f1d256f. Report an issue: GitHub.