{"record":{"id":"fa63f81799b054b3","repo":"spacedriveapp/spacedrive","slug":"source-path-is-not-local","errorCode":null,"errorMessage":"Source path is not local","messagePattern":"Source path is not local","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/src/ops/files/copy/strategy.rs","lineNumber":89,"sourceCode":"\t) -> Result<u64>;\n}\n\n/// Strategy for an atomic move on the same volume\npub struct LocalMoveStrategy;\n\n#[async_trait]\nimpl CopyStrategy for LocalMoveStrategy {\n\tasync fn execute<'a>(\n\t\t&self,\n\t\tctx: &JobContext<'a>,\n\t\tsource: &SdPath,\n\t\tdestination: &SdPath,\n\t\tverify_checksum: bool,\n\t\tprogress_callback: Option<&ProgressCallback<'a>>,\n\t) -> Result<u64> {\n\t\tlet source_path = source\n\t\t\t.as_local_path()\n\t\t\t.ok_or_else(|| anyhow::anyhow!(\"Source path is not local\"))?;\n\t\tlet dest_path = destination\n\t\t\t.as_local_path()\n\t\t\t.ok_or_else(|| anyhow::anyhow!(\"Destination path is not local\"))?;\n\n\t\t// Read size before rename since source path becomes invalid after move.\n\t\tlet metadata = fs::metadata(source_path).await?;\n\t\tlet size = if metadata.is_file() {\n\t\t\tmetadata.len()\n\t\t} else {\n\t\t\tget_path_size(source_path).await?\n\t\t};\n\n\t\t// Send initial progress event so UI shows 0% before the instant rename.\n\t\tif let Some(callback) = progress_callback {\n\t\t\tcallback(0, size);\n\t\t}\n\n\t\tif let Some(parent) = dest_path.parent() {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/spacedriveapp/spacedrive/blob/6dfeccf2113039e35f2ce735f945e70dc3e4ea45/core/src/ops/files/copy/strategy.rs#L71-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet bytes = LocalMoveStrategy.execute(ctx, &source, &dest, false, None).await?;\n\n// after\nlet strategy = if source.is_local() {\n    &LocalMoveStrategy as &dyn CopyStrategy\n} else {\n    &cross_device_strategy\n};\nlet bytes = strategy.execute(ctx, &source, &dest, false, None).await?;","handlingStrategy":"type-guard","validationCode":"// route only local sources to local strategies\nif !source.is_local() {\n    anyhow::bail!(\"source {} is not on this device\", source.display());\n}","typeGuard":"// SdPath already exposes the guard: is_local() covers all four variants\npub fn requires_local_access(source: &SdPath) -> bool {\n    source.is_local()\n}","tryCatchPattern":"// catch around strategy dispatch and re-route instead of failing the job\nmatch LocalMoveStrategy.execute(ctx, source, dest, verify, cb).await {\n    Err(e) if e.to_string().contains(\"not local\") => {\n        cross_device_copy(ctx, source, dest).await\n    }\n    other => other,\n}","preventionTips":["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."],"tags":["files","copy","sd-path","multi-device"],"backgroundTag":null,"analyzedSha":"6dfeccf2113039e35f2ce735f945e70dc3e4ea45","analyzedAt":"2026-08-16T11:26:17.074Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}