Hmbown/CodeWhale · error · Error
Wait for the pet recorder write before closing.
Error message
Wait for the pet recorder write before closing.
What it means
close() refuses to run while an append is still in flight (busy flag set). This protects the final fsync/close sequence from interleaving with an active write that still needs the output handle and lock.
Solutions
- Track in-flight appends and await them (e.g. Promise.all of pending appends) before calling close().
- Move close() after the producer loop fully drains.
- Use an async mutex/queue so close() waits behind pending writes.
- If close() is called by a signal handler, set a flag, drain writes, then close.
Example fix
// before await recorder.append(last); close(); // sync, races in-flight writes // after await recorder.append(last); await recorder.close();
Defensive patterns
Strategy: try-catch
Validate before calling
if (pendingAppends.size > 0) throw new Error('cannot close: appends still in flight'); Try / catch
try {
await Promise.all([...pendingAppends]);
await recorder.close();
} catch (e) {
if (e.message.includes('Wait for the pet recorder write')) {
await Promise.allSettled([...pendingAppends]);
await recorder.close();
} else throw e;
} Prevention
- Track every in-flight append promise and drain before close
- In signal handlers: set a stopping flag, drain, then close
- Never call close() concurrently with append()
- Wrap close in a queue so it serializes behind writes
When it happens
Trigger: Calling close() without awaiting a pending append() promise; a shutdown handler closing the recorder while a write/rotation is mid-flight; a timeout path calling close() concurrently with a slow rotation.
Common situations: SIGTERM handlers that close the recorder synchronously while a queued append is running; forgetting to await append in a batch loop; finally-block ordering that closes before draining writes.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- engine event channel closed before turn
- Message dispatch belongs to a previous session
- Pet recorder is closed or already writing.
- Runtime engine shutdown failed
- Runtime is shutting down; execution admission is closed
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/b69ef0a26544652c.
Report an issue: GitHub.
Appendix: source
Thrown at pet/scripts/lib/pet-recorder.mjs:166
if (reopened.dev !== nextIdentity.dev || reopened.ino !== nextIdentity.ino)
throw new Error('The live pet recording was replaced externally after rotation.');
expectedMtime = reopened.mtimeNs;
await syncDirectory(dirname(path));
report(`Archived pet recording: ${archive}`);
} finally {
await next?.close();
if (created && !installed) await unlink(temporary);
}
return;
} else {
await output.writeFile(row);
expectedMtime = (await output.stat({ bigint: true })).mtimeNs;
}
bytes += size; sequence++;
} finally { busy = false; }
},
async close() {
if (busy) throw new Error('Wait for the pet recorder write before closing.');
const current = output, heldLock = lock; output = undefined; lock = undefined;
try { if (current) { try { await current.sync(); } finally { await current.close(); } } }
finally { heldLock?.close(); }
},
};
}
View on GitHub (pinned to 433685b202)