Skip to content

Bug: openclaw backup create fails with ENOENT when session file is cleaned up during backup #67417

@legupsystems

Description

@legupsystems

Bug Description

openclaw backup create fails with ENOENT when a session file is cleaned up (deleted by the gateway's session compaction/cleanup process) between when the backup command enumerates all session files and when it tries to archive them.

Exact Error

Error: ENOENT: no such file or directory, lstat '\"/path/to/.openclaw/agents/main/sessions/28cd361b-3f8f-4852-8a31-f7ffc31ede55.jsonl\"\
Process exited with code 1

Steps to Reproduce

  1. Run the OpenClaw gateway with active sessions
  2. Trigger openclaw backup create (e.g., via cron job)
  3. While backup create is enumerating session files in agents/<agent>/sessions/, the gateway's session compaction or cleanup process deletes one or more of those session .jsonl files
  4. When backup create later tries to lstat the now-deleted session file, it throws ENOENT and the entire backup fails

Environment

  • OpenClaw Version: 2026.4.14 (commit 323493f)
  • OS: Linux (any)
  • Trigger: Concurrent session compaction/cleanup running alongside backup create

Root Cause

This is a race condition: the backup command scans for all session files first, then attempts to archive them. If the gateway's compaction process (which runs periodically or on schedule) deletes session files between the scan phase and the archive phase, the backup fails entirely.

The existing session compaction behavior is tracked in issue #15404 (closed, but the underlying behavior may still cause this race).

Expected Behavior

The backup command should gracefully skip session files that no longer exist rather than failing the entire backup.

Proposed Fix

Before adding each session file to the tar archive, check whether it still exists on disk. If it was cleaned up by compaction, skip it with a warning and continue. The backup should succeed with a note in the output that N session files were skipped due to concurrent cleanup.

A simple pattern for the fix in the session-archiving loop:

for (const sessionFile of sessionFiles) {
  try {
    await stat(sessionFilePath)
  } catch (e: unknown) {
    if (isENOENT(e)) {
      logWarning(\`Skipping session \${sessionFile}: file no longer exists (cleaned up by compaction)\`)
      continue
    }
    throw e
  }
  // proceed to archive
}

This mirrors how the codebase already handles ENOENT in comparable situations (e.g., fileHistory.ts uses the same pattern for deleted tracked files).

Workaround

Use --only-config to bypass session archiving entirely, but this loses session history in the backup.

Impact

  • Severity: Medium — backup fails entirely due to a transient, expected condition (session cleanup)
  • Users running backup create on a schedule while the gateway is running will experience intermittent backup failures
  • No data is lost, but the backup artifact is not produced

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions