Hmbown/CodeWhale · error

event transaction runs once

Error message

event transaction runs once

What it means

Panic inside the event-file transaction retry loop: `operation.take().expect("event transaction runs once")` asserts the closure is only consumed once per loop iteration. Because `operation` is reset to `Some(operation)` each iteration before `try_write`, the expect should be unreachable; it panics only if the closure was already taken (a control-flow bug where the loop body could execute twice on one assignment).

Solutions

  1. Keep the `operation = Some(operation)` re-assignment at the top of each loop iteration intact
  2. Restructure to pass the closure by value into the match arm instead of Option::take to make double-consumption impossible
  3. If the panic fires, audit recent changes to the retry loop for a second take() path

Example fix

// before
let mut operation = Some(operation);
loop {
    match lock.try_write().map(|_g| operation.take().expect("event transaction runs once")()) {
// after
loop {
    match lock.try_write() {
        Ok(_guard) => return operation(),
        Err(error) if would_block_or_interrupted => { /* retry */ }
    }
Defensive patterns

Strategy: type-guard

Type guard

if let Some(run) = operation.take() { ... } // never unwrap/take without re-arming

Try / catch

// Avoid Option::take; move the closure into the arm so double-use is a compile error
match lock.try_write() { Ok(_) => return operation(), Err(e) => handle(e) }

Prevention

When it happens

Trigger: A future refactor of the retry loop that calls the closure more than once, or `try_write` succeeding spuriously after `operation` was consumed — i.e., the loop body running without re-arming `operation`.

Common situations: Code review/refactor of the fd_lock retry logic introducing a second `take()` call or missing the reset of `operation` at the top of the loop.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/710aa46beea8ae03. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:1855

            use std::os::unix::fs::PermissionsExt as _;
            file.set_permissions(fs::Permissions::from_mode(0o600))
                .context("Failed to secure Runtime event lock")?;
        }
        Ok(file)
    }

    fn with_event_transaction<T>(
        &self,
        timeout: Duration,
        operation: impl FnOnce() -> Result<T>,
    ) -> Result<T> {
        let mut lock = fd_lock::RwLock::new(self.open_event_lock()?);
        let started = Instant::now();
        let mut operation = Some(operation);
        loop {
            match lock
                .try_write()
                .map(|_guard| operation.take().expect("event transaction runs once")())
            {
                Ok(result) => return result,
                Err(error)
                    if matches!(
                        error.kind(),
                        std::io::ErrorKind::WouldBlock | std::io::ErrorKind::Interrupted
                    ) =>
                {
                    wait_for_event_lock(started, timeout)?;
                }
                Err(error) => return Err(error).context("Failed to lock Runtime events"),
            }
        }
    }

    fn record_path(base: &Path, id: &str, extension: &str, label: &str) -> Result<PathBuf> {
        let id = validated_record_id(id, label)?;
        Ok(base.join(format!("{id}.{extension}")))

View on GitHub (pinned to 73e0f67d83)