ruvnet/ruflo · error · Error

hexToBytes: odd-length hex string

Error message

hexToBytes: odd-length hex string

What it means

session resume loads <sessionId>.json from .claude-flow/sessions/ under process.cwd() inside a single try block that covers file read AND JSON.parse; any failure in that block is collapsed into 'Session not found: <id>'. So the message can mean the file is genuinely missing, but also that it is unreadable or corrupt — and since sessions live on disk keyed by the server's working directory, resuming from a different cwd guarantees the miss. Checksum mismatches do NOT throw; they only append to an errors array.

Source

Thrown at plugins/ruflo-neural-trader/src/signed-attribution.ts:386

    name: item.name,
    score: item.score,
    rank: i + 1,
  }));
}

/* ---------------------------------------------------------------------- */
/* Helpers                                                                */
/* ---------------------------------------------------------------------- */

function canonicalBytes(body: SignedAttributionArtifactBody): Uint8Array {
  const message = JSON.stringify(body);
  return new TextEncoder().encode(message);
}

function hexToBytes(hex: string): Uint8Array {
  const clean = hex.replace(/^0x/, '');
  if (clean.length % 2 !== 0) {
    throw new Error('hexToBytes: odd-length hex string');
  }
  const out = new Uint8Array(clean.length / 2);
  for (let i = 0; i < out.length; i++) {
    out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
  }
  return out;
}

function bytesToHex(bytes: Uint8Array): string {
  let s = '';
  for (let i = 0; i < bytes.length; i++) {
    s += bytes[i].toString(16).padStart(2, '0');
  }
  return s;
}

/**
 * Mulberry32 — small, fast, deterministic PRNG. Same algorithm everywhere

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Verify the file exists before resuming: ls .claude-flow/sessions/<sessionId>.json from the server's working directory
  2. Run the MCP server from the same directory the session was created in — the session store is rooted at process.cwd()
  3. List available sessions first (session files in .claude-flow/sessions/) and confirm the exact ID including case
  4. If the file exists but is corrupt (0 bytes / cut off), delete it and start a fresh session — the error message hides JSON.parse failures, so test with: node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'))" .claude-flow/sessions/<id>.json
  5. Persist .claude-flow/sessions across container restarts via a mounted volume

Example fix

// before
await client.callTool('session_resume', { sessionId }); // throws [1127] when run from another cwd

// after: pre-check existence relative to the server's cwd
import { existsSync } from 'fs';
import { join } from 'path';
const file = join(process.cwd(), '.claude-flow/sessions', `${sessionId}.json`);
if (!existsSync(file)) throw new Error(`No session file at ${file} — start the MCP server in the session's original directory`);
await client.callTool('session_resume', { sessionId });
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, readFileSync } from 'fs';
import { join } from 'path';

function sessionFileReadable(sessionId: string, cwd = process.cwd()): boolean {
  const file = join(cwd, '.claude-flow/sessions', `${sessionId}.json`);
  if (!existsSync(file)) return false;
  try { JSON.parse(readFileSync(file, 'utf-8')); return true; } // catches the corrupt-JSON case
  catch { return false; }
}

Try / catch

try {
  await client.callTool('session_resume', { sessionId });
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Session not found')) {
    // message also covers unreadable/corrupt files — check the file directly to distinguish
    if (!sessionFileReadable(sessionId)) throw new Error(`session ${sessionId} missing or corrupt under cwd`);
  }
  throw e;
}

Prevention

When it happens

Trigger: session_resume with an ID that was never saved or was saved in another process run from a different directory; the .claude-flow/sessions/<id>.json file was deleted or never flushed (crash before save); the JSON file is truncated/corrupt so JSON.parse throws and gets swallowed into the same message; sessionId casing mismatch on case-sensitive filesystems.

Common situations: Starting the MCP server from a different directory between runs (sessions are cwd-relative, not global); cleanup scripts wiping .claude-flow/; assuming sessions persist across container restarts when the dir was never mounted.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/46d1a97d7647eea2. Report an issue: GitHub.