{"record":{"id":"e92d4b9b36ed8e89","repo":"astrid-runtime/astrid","slug":"layout-record-has-no-file-name","errorCode":null,"errorMessage":"layout record has no file name","messagePattern":"layout record has no file name","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs_layout.rs","lineNumber":525,"sourceCode":"fn atomic_write(path: &Path, bytes: &[u8]) -> io::Result<()> {\n    #[cfg(windows)]\n    {\n        crate::platform_fs::atomic_write_private_file(path, bytes)\n    }\n\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::OpenOptionsExt as _;\n\n        let parent = path.parent().ok_or_else(|| {\n            io::Error::new(io::ErrorKind::InvalidInput, \"layout record has no parent\")\n        })?;\n        std::fs::create_dir_all(parent)?;\n        let name = path\n            .file_name()\n            .and_then(|name| name.to_str())\n            .ok_or_else(|| {\n                io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    \"layout record has no file name\",\n                )\n            })?;\n        let staged = parent.join(format!(\".{name}.next\"));\n        match std::fs::symlink_metadata(&staged) {\n            Ok(metadata) if metadata.file_type().is_file() => std::fs::remove_file(&staged)?,\n            Ok(_) => {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\"layout staging path is redirected: {}\", staged.display()),\n                ));\n            },\n            Err(error) if error.kind() == io::ErrorKind::NotFound => {},\n            Err(error) => return Err(error),\n        }\n        let mut file = OpenOptions::new()\n            .write(true)","sourceCodeStart":507,"sourceCodeEnd":543,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs_layout.rs#L507-L543","documentation":"atomic_write needs the file name component of the target path to build its staging file (.{name}.next). When path.file_name() is None or is not valid UTF-8 (e.g. the path is \"..\", ends in \"..\", or contains non-UTF-8 bytes on unix), it raises InvalidInput \"layout record has no file name\". This guards against silently mis-staging the atomic write.","triggerScenarios":"Calling write_layout_version with a path that is a directory root, \"..\", ends with a separator, or whose final component is non-UTF-8, so and_then(|name| name.to_str()) yields None.","commonSituations":"Passing a directory instead of a file path; paths built from raw OS bytes with non-UTF-8 components; accidentally joining an empty file-name onto the layout directory.","solutions":["Pass a path with a concrete UTF-8 file name, e.g. layout_dir.join(\"layout-v2.json\").","Check path.file_name().and_then(OsStr::to_str).is_some() before calling.","Reject paths ending in \"..\" or \"/\" in your own path-building code.","Convert non-UTF-8 sources to valid UTF-8 names when constructing the record path."],"exampleFix":"// before\nlet path = layout_dir.join(\"..\");\nwrite_layout_version(&path, &record)?;\n// after\nlet path = layout_dir.join(\"layout-v2.json\");\nwrite_layout_version(&path, &record)?;","handlingStrategy":"validation","validationCode":"fn has_valid_file_name(path: &Path) -> bool {\n    path.file_name().and_then(|n| n.to_str()).is_some()\n}","typeGuard":"fn utf8_file_name(path: &Path) -> Option<&str> {\n    path.file_name().and_then(|n| n.to_str())\n}","tryCatchPattern":"match write_layout_version(path, record) {\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains(\"no file name\") => {\n        eprintln!(\"path must end in a concrete UTF-8 file name: {}\", path.display());\n    },\n    other => other?,\n}","preventionTips":["Never pass paths ending in \"/\", \".\", or \"..\" to layout APIs","Ensure file names are valid UTF-8 when sourced from raw OS bytes","Assert file_name().is_some() in path-construction helpers"],"tags":["filesystem","invalid-path","atomic-write"],"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-14T11:17:12.474Z"}