{"record":{"id":"913e592c2b645df4","repo":"ruvnet/RuView","slug":"parent-not-accessible-e","errorCode":null,"errorMessage":"parent not accessible {}: {e}","messagePattern":"parent not accessible (.+?): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"v2/crates/wifi-densepose-pointcloud/src/training.rs","lineNumber":59,"sourceCode":"    }\n    for comp in child_path.components() {\n        if matches!(comp, std::path::Component::ParentDir) {\n            return Err(anyhow!(\"child path may not contain `..`: {child}\"));\n        }\n    }\n\n    let joined = base.join(child_path);\n    // Canonicalise base (must exist) and verify joined starts with it. If the\n    // joined file doesn't exist yet we canonicalise the parent.\n    let canonical_base = base\n        .canonicalize()\n        .map_err(|e| anyhow!(\"data_dir not accessible {}: {e}\", base.display()))?;\n    let canonical_parent = joined\n        .parent()\n        .ok_or_else(|| anyhow!(\"no parent for {}\", joined.display()))?;\n    let canonical_parent = canonical_parent\n        .canonicalize()\n        .map_err(|e| anyhow!(\"parent not accessible {}: {e}\", canonical_parent.display()))?;\n    if !canonical_parent.starts_with(&canonical_base) {\n        return Err(anyhow!(\n            \"refusing to write outside data_dir: {}\",\n            joined.display()\n        ));\n    }\n    Ok(canonical_parent.join(\n        joined\n            .file_name()\n            .ok_or_else(|| anyhow!(\"no filename for {}\", joined.display()))?,\n    ))\n}\n\n/// Training data sample — a snapshot of the scene.\n#[derive(Serialize, Deserialize)]\npub struct TrainingSample {\n    pub timestamp_ms: i64,\n    pub source: String,","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/v2/crates/wifi-densepose-pointcloud/src/training.rs#L41-L77","documentation":"After rejecting `..` components and joining the child name onto `data_dir`, `safe_join` canonicalizes the *parent* of the joined path to prove containment. If that parent does not exist — typically because the child name contains a subdirectory that was never created (`subdir/frames.json` with no `subdir/`) — or is not accessible, or a path component meant to be a directory is actually a regular file (ENOTDIR), this error fires.","triggerScenarios":"Writing `subdir/file.json` through the training API without creating `subdir` first; a nested name whose parent component collides with an existing regular file; concurrent processes renaming/removing directories between join and canonicalize.","commonSituations":"Callers assuming the API mkdirs intermediate directories (it deliberately does not); capture pipelines introducing date-scoped subpaths; leftover files occupying a directory name.","solutions":["Create the intermediate directory yourself before the write: `std::fs::create_dir_all(data_dir.join(\"subdir\"))?;`.","Use flat, single-component filenames for children of data_dir.","If the error text shows ENOTDIR, remove or rename the regular file occupying the parent-component name."],"exampleFix":"// before\nsession.write_child(\"day1/frames.json\", &data)?; // parent not accessible .../day1\n// after\nstd::fs::create_dir_all(session.data_dir().join(\"day1\"))?;\nsession.write_child(\"day1/frames.json\", &data)?;","handlingStrategy":"validation","validationCode":"fn child_parent_ready(base: &std::path::Path, child: &str) -> bool {\n    let joined = base.join(child);\n    match joined.parent() {\n        Some(p) => p.is_dir(),\n        None => false,\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only pass single-component filenames to safe_join'd write APIs; create subdirectories explicitly with create_dir_all first.","Never name a file and later a directory with the same component — ENOTDIR is the tell.","In tests, assert parent readiness before exercising write paths."],"tags":["rust","filesystem","io","path-handling","training"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}