spacedriveapp/spacedrive · error
Source path is not local
Error message
Source path is not local
What it means
LocalMoveStrategy::execute (core/src/ops/files/copy/strategy.rs:89) calls SdPath::as_local_path() on the source, which returns None for every non-Physical SdPath variant (Cloud, Content, Sidecar) and for Physical paths whose device_slug is not the current device (not 'local', not this device's slug, not this device's UUID — see is_current_device in core/src/domain/addressing.rs:204). Move-by-rename only works on files this process can open locally, so a remote/cloud source is rejected immediately.
Source
Thrown at core/src/ops/files/copy/strategy.rs:89
) -> Result<u64>;
}
/// Strategy for an atomic move on the same volume
pub struct LocalMoveStrategy;
#[async_trait]
impl CopyStrategy for LocalMoveStrategy {
async fn execute<'a>(
&self,
ctx: &JobContext<'a>,
source: &SdPath,
destination: &SdPath,
verify_checksum: bool,
progress_callback: Option<&ProgressCallback<'a>>,
) -> Result<u64> {
let source_path = source
.as_local_path()
.ok_or_else(|| anyhow::anyhow!("Source path is not local"))?;
let dest_path = destination
.as_local_path()
.ok_or_else(|| anyhow::anyhow!("Destination path is not local"))?;
// Read size before rename since source path becomes invalid after move.
let metadata = fs::metadata(source_path).await?;
let size = if metadata.is_file() {
metadata.len()
} else {
get_path_size(source_path).await?
};
// Send initial progress event so UI shows 0% before the instant rename.
if let Some(callback) = progress_callback {
callback(0, size);
}
if let Some(parent) = dest_path.parent() {View on GitHub (pinned to 6dfeccf211)
Solutions
- Before dispatch, check `source.is_local()` and route non-local sources to the cross-device strategy (execute_push/pull) or download step instead of LocalMoveStrategy.
- If the file should be local, rebuild the SdPath for the current device: Physical { device_slug: "local", path } or the current device slug.
- Verify device identity is stable: the data dir (which holds the device identity) must not have been recreated between path creation and move.
Example fix
// before
let bytes = LocalMoveStrategy.execute(ctx, &source, &dest, false, None).await?;
// after
let strategy = if source.is_local() {
&LocalMoveStrategy as &dyn CopyStrategy
} else {
&cross_device_strategy
};
let bytes = strategy.execute(ctx, &source, &dest, false, None).await?; Defensive patterns
Strategy: type-guard
Validate before calling
// route only local sources to local strategies
if !source.is_local() {
anyhow::bail!("source {} is not on this device", source.display());
} Type guard
// SdPath already exposes the guard: is_local() covers all four variants
pub fn requires_local_access(source: &SdPath) -> bool {
source.is_local()
} Try / catch
// catch around strategy dispatch and re-route instead of failing the job
match LocalMoveStrategy.execute(ctx, source, dest, verify, cb).await {
Err(e) if e.to_string().contains("not local") => {
cross_device_copy(ctx, source, dest).await
}
other => other,
} Prevention
- Check is_local() on both ends before selecting any local copy/move strategy.
- Build SdPath values with the current device slug ('local') for local files.
- Log SdPath kind + slug at job start to catch misrouting early.
When it happens
Trigger: Dispatching a move where source is an SdPath::Cloud ('s3://...'), SdPath::Content ('content://<uuid>'), or SdPath::Physical belonging to a paired peer device (device slug of another machine). Also happens after the local device identity changes (fresh data dir), making previously-local slugs no longer match.
Common situations: Copy/move job receives a destination-relative or cloud path by mistake; multi-device library where a file pinned from another device is moved; device re-provisioned so get_current_device_slug() changed; UI constructing SdPath from a display URI like 'local://other-device/...' without resolving to the local device.
Related errors
- Destination path is not local
- Destination must have a device slug for cross-device transfe
- Checksum verification failed
- Source must be local path for PUSH operation
- Source must be a physical path for PULL operation
AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16).
Data as JSON: /api/errors/fa63f81799b054b3.
Report an issue: GitHub.