{"record":{"id":"5f7eb5eed6c6b0a0","repo":"astrid-runtime/astrid","slug":"failed-to-write-readiness-file-daemon-is-useless","errorCode":null,"errorMessage":"Failed to write readiness file (daemon is useless without it): {e}","messagePattern":"Failed to write readiness file \\(daemon is useless without it\\): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/astrid-daemon/src/lib.rs","lineNumber":365,"sourceCode":"                error = %error,\n                principal = %install_principal,\n                \"install principal is not admitted; canonical astrid-contracts.wit was not refreshed\"\n            );\n        },\n    }\n\n    // Signal readiness AFTER the default CLI/system view is loaded and\n    // accepting connections. The CLI polls for this file to avoid connecting\n    // before the handshake accept loop is running.\n    write_readiness_then_arm_ephemeral(\n        args.ephemeral,\n        || {\n            astrid_kernel::socket::write_readiness_file_for_workspace(\n                &kernel.workspace_root,\n                kernel.workspace_layout(),\n            )\n            .map_err(|e| {\n                anyhow::anyhow!(\n                    \"Failed to write readiness file \\\n                     (daemon is useless without it): {e}\"\n                )\n            })\n        },\n        || {\n            // Only now can a spawning client connect. Starting this timer\n            // during Kernel construction lets a cold capsule boot consume\n            // the entire grace period and makes the daemon advertise\n            // readiness after shutdown was already requested.\n            kernel.arm_ephemeral_startup_fallback();\n        },\n    )?;\n\n    tracing::info!(\n        session = %session_id.0,\n        ephemeral = args.ephemeral,\n        \"Kernel booted successfully\"","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-daemon/src/lib.rs#L347-L383","documentation":"The daemon writes a readiness file for the workspace (`write_readiness_file_for_workspace`) so clients can discover it. The write is executed in a race-tolerant block, and if it fails the daemon treats itself as useless and aborts with this message, since nothing can connect to a daemon whose readiness file is absent.","triggerScenarios":"Daemon `run()` reaching the readiness-file step where the filesystem write fails: unwritable directory, disk full, or the workspace layout has no valid target path.","commonSituations":"Read-only filesystem or changed permissions on the workspace/durable root after the daemon started; disk quota exhaustion on CI machines.","solutions":["Check the wrapped `{e}` I/O error for the concrete cause.","Ensure the workspace root and its layout directories are writable by the daemon user.","Free disk space / fix quota issues, then restart the daemon."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// shell\n[ -w \"$WORKSPACE_ROOT\" ] || { echo \"workspace not writable\" >&2; exit 1; }\n[ \"$(df --output=avail \"$WORKSPACE_ROOT\" | tail -1)\" -gt 1024 ] || { echo \"low disk\" >&2; exit 1; }","typeGuard":null,"tryCatchPattern":"// rust\nif let Err(e) = daemon::run(args).await {\n    if e.to_string().contains(\"readiness file\") {\n        eprintln!(\"fix workspace write access/disk space, then retry: {e:#}\");\n    }\n    std::process::exit(1);\n}","preventionTips":["Ensure the daemon user owns write access to the workspace root.","Monitor disk space/quota on machines hosting daemons.","Avoid mounting the workspace read-only after boot."],"tags":["filesystem","readiness","startup"],"backgroundTag":"file-write-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}