{"record":{"id":"1c0f34eca7df40a8","repo":"Hmbown/CodeWhale","slug":"config-path-must-include-a-file-name","errorCode":null,"errorMessage":"config path must include a file name","messagePattern":"config path must include a file name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/lib.rs","lineNumber":6766,"sourceCode":"            toml::Value::Table(redacted)\n        }\n        _ if sensitive => toml::Value::String(\"********\".to_string()),\n        _ => value.clone(),\n    }\n}\n\nfn normalize_config_file_path(path: PathBuf) -> Result<PathBuf> {\n    if path.as_os_str().is_empty() {\n        bail!(\"config path cannot be empty\");\n    }\n    if path\n        .components()\n        .any(|component| matches!(component, Component::ParentDir))\n    {\n        bail!(\"config path cannot contain '..' components\");\n    }\n    if path.file_name().is_none() {\n        bail!(\"config path must include a file name\");\n    }\n    let absolute = if path.is_absolute() {\n        path\n    } else {\n        std::env::current_dir()\n            .context(\"failed to resolve current directory for config path\")?\n            .join(path)\n    };\n    let file_name = absolute\n        .file_name()\n        .map(OsString::from)\n        .context(\"config path must include a file name\")?;\n    let parent = absolute\n        .parent()\n        .context(\"config path must include a parent directory\")?;\n    let parent = match parent.canonicalize() {\n        Ok(parent) => parent,\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => parent.to_path_buf(),","sourceCodeStart":6748,"sourceCodeEnd":6784,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/lib.rs#L6748-L6784","documentation":"normalize_config_file_path requires the path to name a file: after rejecting '..' components it checks path.file_name() and fails if there is none. Paths ending in '/', '.', '..', or a filesystem root do not identify a config file, so the library refuses them.","triggerScenarios":"Passing \"/\" (root), \"/etc/\", \".\", \"..\", or any path whose final component is a directory indicator to the config loader.","commonSituations":"Pointing the loader at a config directory instead of the file inside it, or a template placeholder like \"/config/\" left unsubstituted in a deployment manifest.","solutions":["Pass the full path including the file name, e.g. \"/home/user/.config/codewhale/config.toml\"","If you have a directory, append the expected file name before loading","Check for trailing slashes or '.' placeholders in generated paths"],"exampleFix":"// before\nload_config(PathBuf::from(\"/home/user/.config/codewhale/\"))\n// after\nload_config(PathBuf::from(\"/home/user/.config/codewhale/config.toml\"))","handlingStrategy":"validation","validationCode":"fn names_a_file(p: &std::path::Path) -> bool {\n    p.file_name().is_some()\n}","typeGuard":"fn with_file_name(p: &std::path::Path, name: &str) -> std::path::PathBuf {\n    if p.file_name().is_some() { p.to_path_buf() } else { p.join(name) }\n}","tryCatchPattern":"match normalize_config_file_path(path) {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"must include a file name\") => {\n        eprintln!(\"point --config at the file, not a directory\");\n        std::process::exit(2);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Include the file name (config.toml) in every config-path argument","Check for trailing slashes in generated deployment manifests","Distinguish directory-type settings from file-type settings in your tooling"],"tags":["config","path","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}