mastra-ai/mastra · error

${file} was opened by another process during compaction — is

Error message

${file} was opened by another process during compaction — is another Mastra Code session running? Close other sessions and run /prune vacuum again.

What it means

VACUUM INTO can take tens of seconds on large databases. After compaction finishes and before swapping the compacted copy into place, reclaimLibSQLDisk re-probes the journal-mode header. If it is no longer 'delete', a new session opened the database in WAL mode during the vacuum window; swapping under it would orphan that session's inode and silently lose its writes, so the tool deletes the temp copy and aborts.

Source

Thrown at mastracode/sdk/src/utils/storage-maintenance.ts:216

          `Not enough free disk space to compact ${file}: need ${formatBytes(requiredFreeBytes(liveBytes))} ` +
            `for a ${formatBytes(liveBytes)} compacted copy, but only ${formatBytes(freeBytes)} is free.`,
        );
      }
      onFileStart?.(file, bytesBefore, liveBytes);
      db.exec(`VACUUM INTO '${tmp.replace(/'/g, "''")}'`);
    } catch (err) {
      rmSync(tmp, { force: true });
      throw err;
    } finally {
      db.close();
    }
    // Re-probe right before the swap: `VACUUM INTO` can take tens of seconds
    // on multi-GB files, and a session started in that window reopens the db
    // in WAL mode (flipping the header back). Swapping under it would orphan
    // that session's inode and silently lose its writes.
    if (journalModeFromHeader(file) !== 'delete') {
      rmSync(tmp, { force: true });
      throw new Error(
        `${file} was opened by another process during compaction — is another Mastra Code session running? ` +
          `Close other sessions and run /prune vacuum again.`,
      );
    }
    // Swap the compacted copy into place. The old WAL/SHM sidecars belong to
    // the old inode — they must never be paired with the new file. If any step
    // after the first rename fails, restore the original so the db path is
    // never left empty (a naive restart would otherwise create a fresh db).
    renameSync(file, `${file}.old`);
    try {
      rmSync(`${file}-wal`, { force: true });
      rmSync(`${file}-shm`, { force: true });
      renameSync(tmp, file);
    } catch (err) {
      try {
        renameSync(`${file}.old`, file);
      } catch {
        throw new Error(

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Re-run /prune vacuum with no other sessions able to start: stop dev servers, file watchers, and auto-restart policies
  2. Repeat the vacuum — the aborted run cleaned up its temp file, no data was lost
  3. Vacuum during a quiet window (e.g. before starting the TUI, in CI, or maintenance downtime)
Defensive patterns

Strategy: retry

Validate before calling

import { readFileSync } from 'node:fs';
// Re-check just before starting the vacuum that nothing else has the db open
const inWalMode = readFileSync(dbFile).subarray(18, 19).toString() === String.fromCharCode(2);
if (inWalMode) throw new Error('Aborting: db opened in WAL mode by another process.');

Try / catch

try {
  await maintenance.results();
} catch (e) {
  if (String(e.message).includes('was opened by another process during compaction')) {
    console.error('No data lost. Ensure no sessions can start (disable watchers/auto-restart), then re-run the vacuum.');
  } else throw e;
}

Prevention

When it happens

Trigger: A Mastra Code session (or any libsql client) starts and opens the storage db in WAL mode while a long-running /prune vacuum is compacting it; the re-probe just before the rename detects the header flip.

Common situations: A teammate or a watcher (dev server, background agent) starts the app while you vacuum a multi-GB database; an auto-restart (nodemon, docker restart policy) respawns a session mid-vacuum.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/4dda85f634bd518f. Report an issue: GitHub.