remix-run/react-router · error · Error

Error generating session

Error message

Error generating session

What it means

Thrown by createFileSessionStorage's createData when getFile(dir, id) returns null. getFile validates the id against /^[0-9a-f]{16}$/i (16 hex chars). Because the id is derived from 8 random bytes hex-encoded (always 16 lowercase hex chars), this guard is effectively a defensive invariant — if it fires, something tampered with the id or getFile's regex, or the environment's Buffer/crypto produced an unexpected value. The EEXIST case (file already exists) is retried in the while-loop, not surfaced here.

Source

Thrown at packages/react-router-node/sessions/fileStorage.ts:51

  dir,
}: FileSessionStorageOptions): SessionStorage<Data, FlashData> {
  return createSessionStorage({
    cookie,
    async createData(data, expires) {
      let content = JSON.stringify({ data, expires });

      while (true) {
        let randomBytes = crypto.getRandomValues(new Uint8Array(8));
        // This storage manages an id space of 2^64 ids, which is far greater
        // than the maximum number of files allowed on an NTFS or ext4 volume
        // (2^32). However, the larger id space should help to avoid collisions
        // with existing ids when creating new sessions, which speeds things up.
        let id = Buffer.from(randomBytes).toString("hex");

        try {
          let file = getFile(dir, id);
          if (!file) {
            throw new Error("Error generating session");
          }
          await fsp.mkdir(path.dirname(file), { recursive: true });
          await fsp.writeFile(file, content, { encoding: "utf-8", flag: "wx" });
          return id;
        } catch (error: any) {
          if (error.code !== "EEXIST") throw error;
        }
      }
    },
    async readData(id) {
      try {
        let file = getFile(dir, id);
        if (!file) {
          return null;
        }
        let content = JSON.parse(await fsp.readFile(file, "utf-8"));
        let data = content.data;
        let expires =

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Confirm the Node runtime supports globalThis.crypto.getRandomValues (Node 19+ has it global; older needs the WebCrypto polyfill).
  2. If stubbing crypto in tests, ensure getRandomValues fills exactly 8 bytes with random values.
  3. Avoid monkeypatching Buffer.prototype.toString or the fileStorage module.
  4. Upgrade @react-router/node to the latest patch in case the id-generation contract changed.

Example fix

// test setup — before (breaks id generation)
globalThis.crypto = { getRandomValues: () => new Uint8Array([0xff]) }; // wrong length
// after
const { webcrypto } = require('node:crypto');
globalThis.crypto = webcrypto; // real implementation fills the requested 8 bytes
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity check the crypto path used by fileStorage
const bytes = new Uint8Array(8); globalThis.crypto.getRandomValues(bytes);
const id = Buffer.from(bytes).toString('hex');
if (!/^[0-9a-f]{16}$/i.test(id)) throw new Error('crypto.getRandomValues produced an unexpected id shape');

Type guard

function isValidFileSessionId(id: string): boolean {
  return /^[0-9a-f]{16}$/i.test(id);
}

Try / catch

try { await sessionStorage.createData(content); }
catch (e) {
  if (e instanceof Error && e.message === 'Error generating session') {
    // check globalThis.crypto / Buffer polyfills in the runtime
  }
  throw e;
}

Prevention

When it happens

Trigger: The generated id fails the /^[0-9a-f]{16}$/i test inside getFile. Reachable only if crypto.getRandomValues returns something other than 8 bytes, Buffer.from(...).toString('hex') yields non-hex or wrong length, or getFile's regex was monkeypatched. In normal Node this is essentially unreachable.

Common situations: A test environment that stubs crypto.getRandomValues incorrectly. A polyfill that changes Buffer.from/toString behavior. Corruption of the fileStorage module. Not a typical production failure.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/ca64c69f9fec3998. Report an issue: GitHub.