ruvnet/ruflo · error · Error
localSingleEntryPageRank: sourceIndex ${src} out of range [0
Error message
localSingleEntryPageRank: sourceIndex ${src} out of range [0, ${n}) What it means
This is the second, defense-in-depth check in getSessionPath(): after the strict charset validation passes, it path.resolve()s both the assembled session file path and the session directory and requires the file path to start with the directory plus a path separator. With the regex already forbidding '/', '\', and '..', it should be unreachable through the sessionId alone; in practice it fires when the directory itself resolves somewhere unexpected, e.g. process.cwd() contains a symlink so resolvedDir differs from the joined prefix.
Source
Thrown at plugins/ruflo-neural-trader/src/signed-attribution.ts:219
* The math: standard personalized PageRank with the personalization vector
* concentrated entirely on the source node. Forward-push semantics in the
* limit, plain power iteration on a small in-memory graph in practice.
* Seeded so that two runs with the same graph + same seed return byte-
* identical ordering (asserted by the Phase 6 smoke's reproducibility
* check).
*/
export function localSingleEntryPageRank(
graph: AttributionGraph,
opts: PageRankOptions,
): PageRankResult {
const n = graph.nodes.length;
if (n === 0) return { scores: [], iterations: 0 };
const damping = opts.damping ?? 0.85;
const maxIter = opts.maxIterations ?? 100;
const tol = opts.tolerance ?? 1e-8;
const src = opts.sourceIndex;
if (src < 0 || src >= n) {
throw new Error(
`localSingleEntryPageRank: sourceIndex ${src} out of range [0, ${n})`,
);
}
// Personalization vector concentrated on src.
const personalization = new Float64Array(n);
personalization[src] = 1;
// Initialize: seeded deterministic noise then re-normalize so the start
// vector still sums to 1. The seed controls the initialization only —
// PageRank converges to the same stationary distribution regardless, but
// the iteration *order* and the path through the state space depend on
// the seed when ties are present. This is what the smoke asserts.
let rng = mulberry32(opts.seed);
let r = new Float64Array(n);
let sum = 0;
for (let i = 0; i < n; i++) {
// Small positive noise so we don't divide by zero on degenerate graphs.View on GitHub (pinned to fa13ee4ad6)
Solutions
- Start the MCP server from the real (non-symlinked) physical path: cd $(readlink -f .) before launching
- Replace the .claude-flow/sessions symlink with a real directory (or bind mount)
- Regenerate sessions under the corrected directory so old and new paths agree
- If it persists, log path.resolve(cwd) inside getSessionPath to see which prefix mismatch triggers it
Example fix
# before: server started via a symlinked path ln -s /srv/releases/2026-08-18 /srv/current (cd /srv/current && start-mcp-server) # session ops may throw [1126] # after: launch from the physical path cd /srv/releases/2026-08-18 && start-mcp-server
Defensive patterns
Strategy: validation
Validate before calling
import { resolve, join } from 'path';
function sessionPathIsContained(sessionId: string, cwd = process.cwd()): boolean {
const dir = resolve(cwd, '.claude-flow/sessions');
const file = resolve(dir, `${sessionId}.json`);
return file.startsWith(dir + sep);
} Prevention
- Launch MCP servers from physical paths, not symlinked convenience links
- Keep .claude-flow/sessions a real directory, not a symlink
- Treat this error as an environment smell (symlinked cwd) rather than bad input — fix the launch path
When it happens
Trigger: MCP server started in a symlinked directory (e.g. /tmp -> /private/tmp on macOS) where path.resolve(sessionPath) and path.resolve(sessionDir) normalize through different symlink chains; DEFAULT_SESSION_DIR (".claude-flow/sessions") being itself a symlink pointing elsewhere; exotic cwd states (deleted working directory).
Common situations: macOS /tmp symlink issues when running the server from a temp checkout; deployment layouts where the project dir is a symlink (current -> releases/2026-08-18) and different code paths resolve it differently.
Related errors
- unknown strategy "${name}". Available: ${roster.map((r) => r
- signAttributionArtifact: privateKey must be 32 bytes (got ${
- memory path contains disallowed characters
- namespace contains path traversal
- build input escapes repository: ${path}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/0e54f4e44c0d3773.
Report an issue: GitHub.