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
- Keep the `operation = Some(operation)` re-assignment at the top of each loop iteration intact
- Restructure to pass the closure by value into the match arm instead of Option::take to make double-consumption impossible
- 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
- Never consume the closure via take() without resetting it in the same iteration
- Add a loop-invariant comment/test that operation is Some at loop top
- Restructure to pass the closure by reference or re-create it per attempt
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
- event recovery buffer fits u64
- provider authority checked above
- Runtime store root
- snapshots carry one removal token per rule
- turn monitor exits normally only after assigning a terminal…
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)