spacedriveapp/spacedrive · error · anyhow::Error

Path is not local

Error message

Path is not local

What it means

Unreachable through normal data: the match arm is guarded by path.is_local() yet path.as_local_path() returned None immediately after. The two accessors disagree, which can only happen when an SdPath was constructed inconsistently - its kind claims local but it carries no usable local path. Treat it as an invariant violation or data-construction bug, not an environmental condition.

Source

Thrown at core/src/ops/files/delete/strategy.rs:58

pub struct LocalDeleteStrategy;

#[async_trait]
impl DeleteStrategy for LocalDeleteStrategy {
	async fn execute(
		&self,
		ctx: &JobContext<'_>,
		paths: &[SdPath],
		mode: DeleteMode,
	) -> Result<Vec<DeleteResult>> {
		let mut results = Vec::new();

		for path in paths {
			let result = match path {
				// Local physical path - use direct filesystem (fast path)
				_ if path.is_local() => {
					let local_path = path
						.as_local_path()
						.ok_or_else(|| anyhow::anyhow!("Path is not local"))?;

					let size = self.get_path_size(local_path).await.unwrap_or(0);

					let deletion_result = match mode {
						DeleteMode::Trash => self.move_to_trash(local_path).await,
						DeleteMode::Permanent => self.permanent_delete(local_path).await,
						DeleteMode::Secure => self.secure_delete(local_path).await,
					};

					DeleteResult {
						path: path.clone(),
						success: deletion_result.is_ok(),
						bytes_freed: if deletion_result.is_ok() { size } else { 0 },
						error: deletion_result.err().map(|e| e.to_string()),
					}
				}

				// Cloud path - use VolumeBackend

View on GitHub (pinned to 6dfeccf211)

Solutions

  1. Log the full SdPath value at the failure site to identify which construction produced it
  2. Replace the is_local() and as_local_path() pair with a single as_local_path() check
  3. Repair or regenerate persisted path records that deserialize into inconsistent values
  4. Add a property test asserting is_local() implies as_local_path().is_some()

Example fix

// before
_ if path.is_local() => {
    let local_path = path
        .as_local_path()
        .ok_or_else(|| anyhow::anyhow!("Path is not local"))?;
    // ...
}

// after - one authoritative accessor, no invariant to violate
if let Some(local_path) = path.as_local_path() {
    let size = self.get_path_size(local_path).await.unwrap_or(0);
    // ...
} else {
    // route to the remote or volume-backed strategy
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Detect the inconsistency before enqueueing a delete job.
if path.is_local() != path.as_local_path().is_some() {
    anyhow::bail!(
        "inconsistent SdPath: is_local() and as_local_path() disagree for {:?}",
        path
    );
}

Type guard

// Authoritative narrowing: as_local_path() is the single source of truth.
fn as_local(p: &SdPath) -> Option<&std::path::Path> {
    p.as_local_path()
}

match as_local(path) {
    Some(local) => { /* local delete fast path */ }
    None => { /* remote or volume-backed path */ }
}

Prevention

When it happens

Trigger: Hand-constructed SdPath values with a local kind but missing path data; serialization round-trips dropping the local path component; a new SdPath variant where is_local() is true by kind but as_local_path() has no arm covering it.

Common situations: Code building SdPath from raw parts (importers, tests, migrations); deserializing older persisted path records into newer structures.

Related errors


AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16). Data as JSON: /api/errors/ba6a30ff9f855a15. Report an issue: GitHub.