libnyanpasu/clash-nyanpasu · error

managed profile path uses reserved private storage

Error message

managed profile path uses reserved private storage

What it means

ManagedProfilePathResolver::resolve joins a user-supplied managed profile path onto the app's private profiles directory. Before joining it rejects any path component that names the reserved private materialization root, because user-managed files must never be mapped into the private storage tree the app owns. This is the lexical (component-name) check that runs before any filesystem access.

Source

Thrown at backend/tauri/src/service/profile_file.rs:68

            paths,
            self_proxy_port,
            http_timeout: Duration::from_secs(30),
        }
    }

    #[cfg(test)]
    fn with_http_timeout(mut self, timeout: Duration) -> Self {
        self.http_timeout = timeout;
        self
    }

    fn resolve(&self, path: &ManagedProfilePath) -> anyhow::Result<PathBuf> {
        if path
            .as_path()
            .components()
            .any(|component| is_materialization_root_name(component.as_os_str()))
        {
            bail!("managed profile path uses reserved private storage");
        }

        let full = self.paths.app_profiles_dir().join(path.as_path());
        self.validate_existing_parent_chain(&full)?;
        Ok(full)
    }

    fn validate_existing_parent_chain(&self, full: &Path) -> anyhow::Result<()> {
        let root = self.paths.app_profiles_dir();
        let relative = full.strip_prefix(&root).with_context(|| {
            format!(
                "profile path containment violation: {} escapes {}",
                full.display(),
                root.display()
            )
        })?;

        match std::fs::symlink_metadata(&root) {

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Rename the offending profile file/directory so no path component matches the reserved private-storage root name
  2. Keep managed profile paths relative and below the profiles directory, avoiding any component that duplicates internal storage names
  3. If you need to inspect materialized content, use the API/read path for the private root rather than routing it through ManagedProfilePath
  4. Check the path components in your code before constructing the ManagedProfilePath to fail fast with a clearer message

Example fix

// before
let path = ManagedProfilePath::new(".materialized/profile.yaml")?; // reserved component
// after
let path = ManagedProfilePath::new("profiles/profile.yaml")?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_reserved_component(name: &std::ffi::OsStr) -> bool { name == RESERVED_ROOT_NAME } // mirror is_materialization_root_name
pub fn check_managed_path(path: &str) -> Result<(), String> {
    use std::path::Path;
    if Path::new(path).components().any(|c| is_reserved_component(c.as_os_str())) {
        return Err(format!("path {path} contains reserved private storage component"));
    }
    Ok(())
}

Try / catch

match resolver.resolve(&managed_path) {
    Err(e) if e.to_string().contains("reserved private storage") => {
        eprintln!("pick a path outside the reserved storage root, e.g. rename the offending component");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling read/write_atomic/remove/prepare_materialization (all of which funnel through resolve) with a ManagedProfilePath containing a component equal to the materialization root name, e.g. a path literally named after the private storage directory (such as a dot/underscore-prefixed reserved name) or nested like 'sub/<reserved>/file.yaml'.

Common situations: A user naming a profile or folder the same as the app's internal materialization directory; importing a profile list exported from a different app version whose layout used the reserved name; automated scripts deriving profile paths from filesystem listings that included the private root.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/5555744bf002c3e5. Report an issue: GitHub.