{"record":{"id":"28df09f8b9de2ecc","repo":"affaan-m/ECC","slug":"could-not-allocate-a-snapshot-filename-for-session","errorCode":null,"errorMessage":"Could not allocate a snapshot filename for session ${session.sessionId}","messagePattern":"Could not allocate a snapshot filename for session (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/loop-status.js","lineNumber":674,"sourceCode":"  }\n}\n\nfunction getSnapshotPath(outputDir, session, usedNames) {\n  const baseName = sanitizeSnapshotName(session.sessionId);\n  const hashSuffix = hashString(session.transcriptPath || session.sessionId).slice(0, 8);\n  let attempt = 0;\n\n  while (attempt < 1000) {\n    const suffix = attempt === 0 ? '' : `-${hashSuffix}${attempt === 1 ? '' : `-${attempt}`}`;\n    const fileName = `${baseName}${suffix}.json`;\n    if (!usedNames.has(fileName)) {\n      usedNames.add(fileName);\n      return path.join(outputDir, fileName);\n    }\n    attempt += 1;\n  }\n\n  throw new Error(`Could not allocate a snapshot filename for session ${session.sessionId}`);\n}\n\nfunction writeStatusSnapshots(payload, writeDir) {\n  if (!writeDir) {\n    return null;\n  }\n\n  const outputDir = path.resolve(writeDir);\n  fs.mkdirSync(outputDir, { recursive: true });\n\n  const usedNames = new Set(['index.json']);\n  const sessions = payload.sessions.map(session => {\n    const snapshotPath = getSnapshotPath(outputDir, session, usedNames);\n    atomicWriteJson(snapshotPath, {\n      generatedAt: payload.generatedAt,\n      schemaVersion: 'ecc.loop-status.session.v1',\n      session,\n    });","sourceCodeStart":656,"sourceCodeEnd":692,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/loop-status.js#L656-L692","documentation":"When writing status snapshots, the script builds a unique filename per session by appending an empty/hash/numeric suffix and checking a usedNames Set; if 1000 consecutive candidates are all taken it gives up and throws. Reaching it means the baseName plus the hashSuffix plus 0..1000 all collided, which is effectively impossible unless every session in the payload shares the same sessionId-derived baseName (a degenerate input).","triggerScenarios":"A payload.sessions array containing hundreds of sessions whose baseName is identical (e.g. all sessions report the same sessionId, so every hashSuffix is the same and only the counter differentiates — but the counter would still resolve within 1000). Realistically only hit with a corrupted/duplicated session list fed to writeStatusSnapshots.","commonSituations":"A bug upstream that duplicates session records with identical ids. Running writeStatusSnapshots twice into the same writeDir without clearing it, while also feeding an enormous identical-session payload. Essentially a guard rail rather than a user-facing condition.","solutions":["Dedupe the sessions payload before writing so each baseName is distinct.","Use a fresh/empty --write-dir per run to avoid pre-populating usedNames with leftover files.","If genuinely writing >1000 snapshots, shard into multiple output directories.","Treat hitting this error as a data-integrity signal: inspect why sessionIds collide."],"exampleFix":"// before — duplicate sessions cause baseName collisions\nconst sessions = payload.sessions; // contains many dupes\n\n// after — dedupe by sessionId before snapshotting\nconst seen = new Set();\nconst sessions = payload.sessions.filter(s => {\n  if (seen.has(s.sessionId)) return false;\n  seen.add(s.sessionId);\n  return true;\n});","handlingStrategy":"fallback","validationCode":"// Dedupe sessions by sessionId before snapshotting so baseNames are distinct.\nfunction dedupeSessions(sessions) {\n  const seen = new Set();\n  return sessions.filter(s => {\n    if (!s || !s.sessionId || seen.has(s.sessionId)) return false;\n    seen.add(s.sessionId);\n    return true;\n  });\n}","typeGuard":null,"tryCatchPattern":"try { writeStatusSnapshots(payload, writeDir); }\ncatch (err) {\n  if (/Could not allocate a snapshot filename/.test(err.message)) {\n    // Fallback: shard into a subdir per batch, or clear writeDir and retry with deduped payload.\n    writeStatusSnapshots({ ...payload, sessions: dedupeSessions(payload.sessions) }, writeDir);\n  } else throw err;\n}","preventionTips":["Dedupe sessions by sessionId upstream of snapshot writing.","Use a fresh --write-dir per run to avoid pre-seeded collisions.","Treat this error as a data-integrity alarm — inspect the payload, do not just retry."],"tags":["filesystem","snapshots","edge-case","data-integrity"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}