BigPizzaV3/CodexPlusPlus · error
Invalid native cleanup receipt
Error message
Invalid native cleanup receipt
What it means
wait_for_monitor_shutdown_at polls monitor.lock until the previous monitor process releases its exclusive lock (meaning cleanup finished). Once the lock is acquired, it validates the cleanup receipt written there: the file must be at most 1024 bytes. This error means the lock was free but the receipt file is larger than the allowed 1024 bytes, so it cannot be a valid MonitorReceipt and the library refuses to trust it.
Solutions
- Confirm the size: `ls -l <state_root>/monitor.lock` (or `dir` on Windows); anything over 1024 bytes is invalid.
- Delete the corrupt monitor.lock and let the next monitor run recreate it with a fresh receipt.
- Check the diagnostic log (native_browser.compatibility entries) to find which generation/process wrote the oversized file.
- Ensure all CodexPlusPlus processes/builds on the machine use the same version so receipt format matches.
Example fix
// before: oversized receipt $ ls -l monitor.lock ; -rw-r--r-- 4096 monitor.lock // after rm monitor.lock && rerun monitor start # fresh 1024-byte-capped receipt
Defensive patterns
Strategy: try-catch
Validate before calling
let lock = state_root.join("monitor.lock");
if let Ok(meta) = std::fs::metadata(&lock) {
if meta.len() > 1024 {
// receipt is oversized/corrupt: delete monitor.lock and rerun cleanup before waiting
}
} Type guard
fn receipt_size_plausible(p: &std::path::Path) -> bool {
std::fs::metadata(p).map(|m| m.len() <= 1024).unwrap_or(false)
} Try / catch
match wait_for_monitor_shutdown(Duration::from_secs(30)) {
Err(e) if e.to_string().contains("Invalid native cleanup receipt") => {
// treat receipt as corrupt: remove monitor.lock, restore service files from backups, retry
}
other => other?,
} Prevention
- Never write to or append to monitor.lock outside the library
- Shut down monitors through the library's own cleanup path so receipts are truncated properly
- Monitor file sizes in the state dir; an oversized monitor.lock indicates corruption early
When it happens
Trigger: Calling wait_for_monitor_shutdown (Windows) after monitor cleanup when monitor.lock contains more than 1024 bytes — e.g. a corrupted/oversized receipt, garbage appended by another process, repeated writes without truncation, or the file was overwritten by an unrelated tool.
Common situations: A crashed monitor writing a partial/oversized receipt; disk corruption; another process appending to monitor.lock; a user or script editing the lock file; version mismatch where an older/newer build wrote a different format.
Understand the failure class
Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.
Related errors
- Codex CLI 路径指向的是目录而不是可执行文件:
- Codex CLI 没有执行权限:
- Codex CLI 路径不存在: 请填 Codex CLI 可执行文件的完整路径。macOS 桌面版通常在…
- Dream Skin image is empty
- Dream Skin image is not a file
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/e7652845b9d397ab.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/native_browser.rs:782
options.read(true).write(true);
#[cfg(windows)]
{
use std::os::windows::fs::OpenOptionsExt;
options.share_mode(0x1 | 0x2).custom_flags(0x00200000);
}
let mut file = match options.open(&path) {
Ok(file) => file,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
return verify_restored_state(paths);
}
Err(error) => return Err(error.into()),
};
plain_path(&path)?;
let deadline = std::time::Instant::now() + timeout;
loop {
match file.try_lock_exclusive() {
Ok(()) => {
ensure!(file.metadata()?.len() <= 1024, "Invalid native cleanup receipt");
file.seek(SeekFrom::Start(0))?;
let mut bytes = Vec::new();
Read::by_ref(&mut file).take(1025).read_to_end(&mut bytes)?;
let receipt: MonitorReceipt = serde_json::from_slice(&bytes)?;
ensure!(
receipt.schema == 1 && uuid::Uuid::parse_str(&receipt.generation).is_ok()
&& receipt.state == "restored",
"Native browser cleanup did not complete successfully"
);
return Ok(());
}
Err(error) if error.kind() == fs2::lock_contended_error().kind() => {
ensure!(
std::time::Instant::now() < deadline,
"Native browser cleanup is still running; launcher was not terminated"
);
std::thread::sleep(Duration::from_millis(50).min(
deadline.saturating_duration_since(std::time::Instant::now()),View on GitHub (pinned to b1ed92e5e4)