{"record":{"id":"b332cb6957e5d5b3","repo":"astrid-runtime/astrid","slug":"legacy-repl-history-is-not-a-regular-file","errorCode":null,"errorMessage":"legacy REPL history is not a regular file: {}","messagePattern":"legacy REPL history is not a regular file: (.+?)","errorType":"validation","errorClass":"std::io::Error::InvalidData","httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/repl.rs","lineNumber":213,"sourceCode":"                Err(ReadlineError::Eof | _) => {\n                    // Ctrl+D or any I/O error → EOF.\n                    return ReadlineEvent::Eof;\n                },\n            }\n        }\n    }\n}\n\n/// Move the legacy operator history out of the retired top-level Astrid root.\n///\n/// The old file is accepted only as a regular, no-follow file and is bounded\n/// before being copied. If both locations exist, differing bytes fail closed;\n/// an operator must resolve the ambiguity rather than silently lose commands.\nfn migrate_legacy_history(new_path: &Path, legacy_path: &Path) -> io::Result<()> {\n    let legacy_exists = match std::fs::symlink_metadata(legacy_path) {\n        Ok(metadata) => {\n            if metadata.file_type().is_symlink() || !metadata.is_file() {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"legacy REPL history is not a regular file: {}\",\n                        legacy_path.display()\n                    ),\n                ));\n            }\n            astrid_core::platform_fs::verify_no_redirects(legacy_path)?;\n            true\n        },\n        Err(error) if error.kind() == io::ErrorKind::NotFound => false,\n        Err(error) => return Err(error),\n    };\n    if !legacy_exists {\n        return Ok(());\n    }\n    let legacy = read_history_bounded(legacy_path)?;\n    match std::fs::symlink_metadata(new_path) {","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/repl.rs#L195-L231","documentation":"migrate_legacy_history moves an old REPL history file into the new operator log location, but refuses to touch anything that is not a plain regular file. If symlink_metadata shows the legacy path is a symlink, directory, device, or otherwise non-regular, it fails closed with InvalidData so commands are never silently lost through an unsafe migration.","triggerScenarios":"Calling migrate_legacy_history (via Repl::new) when the legacy history path exists but is a symlink or not a regular file (e.g. a directory or fifo at the legacy history location).","commonSituations":"User symlinked their old history file elsewhere (common for dotfile management); a directory was accidentally created at the history path; devtools/tests place a fifo at the path.","solutions":["Remove the symlink/non-regular file at the legacy history path and either restore a real history file or let migration start fresh","If using dotfile symlinks, replace the symlink with a real file (copy the target contents) before running the CLI","Point the operator at the actual file location so nothing is lost, then delete the legacy symlink"],"exampleFix":"// before: ~/.astrid/history -> /dotfiles/history (symlink) causes failure\n// after\nrm ~/.astrid/legacy-history && cp /dotfiles/history ~/.astrid/legacy-history","handlingStrategy":"validation","validationCode":"// Pre-check the legacy history path before triggering migration\nfn legacy_history_is_regular(p: &std::path::Path) -> std::io::Result<bool> {\n    Ok(std::fs::symlink_metadata(p)\n        .map(|m| m.is_file() && !m.file_type().is_symlink())\n        .unwrap_or(true)) // nonexistent is fine (nothing to migrate)\n}","typeGuard":"fn is_regular_file(m: &std::fs::Metadata) -> bool {\n    m.is_file() && !m.file_type().is_symlink()\n}","tryCatchPattern":"match std::fs::symlink_metadata(legacy_path) {\n    Ok(m) if !m.is_file() || m.file_type().is_symlink() => {\n        eprintln!(\"Resolve {}: symlinked/non-regular legacy history must be replaced with a real file\", legacy_path.display());\n    }\n    _ => { /* safe to run REPL */ }\n}","preventionTips":["Do not symlink REPL history files; use copies or hardlinks instead","Check `ls -la` on history paths after restoring backups","Ensure no directory or fifo ever occupies the legacy history path"],"tags":["filesystem","migration","symlink","repl","history"],"backgroundTag":"invalid-argument-value","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}