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
- Rename the offending profile file/directory so no path component matches the reserved private-storage root name
- Keep managed profile paths relative and below the profiles directory, avoiding any component that duplicates internal storage names
- If you need to inspect materialized content, use the API/read path for the private root rather than routing it through ManagedProfilePath
- 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
- Never name profile files/folders after the app's internal materialization root name
- Keep managed paths simple, relative, and below the profiles directory
- When importing/exporting profile sets, sanitize component names against the reserved root name
- Derive managed paths from app APIs, not from raw listings that may include private storage entries
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
- materialization journal operation id mismatch
- non-UTF-8 source
- non-UTF-8 destination
- path is not valid UTF-8: {}
- destination path has no file name: {}
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/5555744bf002c3e5.
Report an issue: GitHub.