Skip to content

set_last_save_timestamp advances unconditionally — auto-save reports healthy while silently failing #162

@pleasedodisturb

Description

@pleasedodisturb

Summary

scripts/continuum_save.sh:fetch_and_run_tmux_resurrect_save_script calls set_last_save_timestamp unconditionally after exec-ing the save script. When the configured save script silently fails (wrong path, missing file, non-zero exit, any error swallowed by >/dev/null 2>&1), the last-save timestamp still advances every interval. Result: @continuum-save-last-timestamp reports healthy saves indefinitely while nothing is being written to disk.

I debugged this in my own setup after losing window layout on a reboot. find ~ -name 'tmux_resurrect_*' -type f returned zero files despite @continuum-save-last-timestamp showing a fresh value updated 15 minutes earlier. The "save" had been failing silently for an unknown duration — possibly weeks.

Current code (master @ 0698e8f)

fetch_and_run_tmux_resurrect_save_script() {
    local resurrect_save_script_path="$(get_tmux_option "$resurrect_save_path_option" "")"
    if [ -n "$resurrect_save_script_path" ]; then
        "$resurrect_save_script_path" "quiet" >/dev/null 2>&1 &   # backgrounded
        set_last_save_timestamp                                    # ← always called
    fi
}

Two issues compound:

  1. & backgrounds the save — the caller never sees the exit code
  2. set_last_save_timestamp runs immediately after, regardless of what the save script does

Reproduction

Targeted reproduction at the function level (works without spinning up a full tmux server). Source the function with stubbed dependencies, point @resurrect-save-script-path at probes with different exit codes:

Case Behaviour Expected
probe exits 0 timestamp advances advances ✓
probe exits 1 timestamp advances should NOT advance
@resurrect-save-script-path points at a missing file timestamp advances should NOT advance
@resurrect-save-script-path unset no-op (no timestamp) no-op ✓

The 2nd and 3rd rows are the bug. Verified both before and after applying the proposed fix below.

Related issues / PRs

Proposed fix (PR forthcoming)

Two changes, ~3 lines of diff:

fetch_and_run_tmux_resurrect_save_script() {
    local resurrect_save_script_path="$(get_tmux_option "$resurrect_save_path_option" "")"
    if [ -n "$resurrect_save_script_path" ]; then
        if "$resurrect_save_script_path" "quiet" >/dev/null 2>&1; then
            set_last_save_timestamp
        fi
    fi
}
  • Drop & — run the save synchronously so the caller sees the exit code
  • Gate set_last_save_timestamp on success — failure leaves the timestamp put and the next tick retries

Behavioural cost: one save-script duration is added to one status refresh per save-interval (default 15 min). Tested locally with pane-contents capture across 10 panes: ~80ms. Invisible at that scale.

Side benefit: the existing acquire_lock trap now correctly holds the lock for the duration of the save. Previously the trap fired on wrapper exit while the backgrounded save was still running, making the lock effectively a no-op for save serialization.

Workaround for users hitting this now (until/unless this lands)

Until a fix lands, I've shipped a wrapper script and a status-bar widget at https://github.com/pleasedodisturb/terminal-craft — the wrapper provides state-aware skip logic and a real-mtime status widget that reads ground truth instead of @continuum-save-last-timestamp. PR #43 there has the full setup if anyone wants to cherry-pick. Files of interest: scripts/continuum-save-guard.sh, scripts/save-status.sh.

Environment

  • tmux 3.6a (Homebrew, macOS arm64)
  • tmux-continuum @ master 0698e8f
  • tmux-resurrect @ latest master
  • bash 3.2.57 (system bash on macOS)

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