{"record":{"id":"02513ec82cfc97a6","repo":"spacedriveapp/spacedrive","slug":"destination-path-is-not-local","errorCode":null,"errorMessage":"Destination path is not local","messagePattern":"Destination path is not local","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/src/ops/files/copy/strategy.rs","lineNumber":92,"sourceCode":"/// 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() {\n\t\t\tfs::create_dir_all(parent).await?;\n\t\t}\n","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/spacedriveapp/spacedrive/blob/6dfeccf2113039e35f2ce735f945e70dc3e4ea45/core/src/ops/files/copy/strategy.rs#L74-L110","documentation":"LocalMoveStrategy::execute (core/src/ops/files/copy/strategy.rs:92) requires the destination SdPath to be local too: as_local_path() returns None for Cloud/Content/Sidecar variants and for Physical paths on another device. A move renames within one filesystem, so a destination the daemon cannot write locally is rejected before any I/O.","triggerScenarios":"Moving a local file to a Cloud path ('s3://bucket/...'), a Content-addressed path, or a Physical path whose device slug names a peer device instead of 'local'/this device.","commonSituations":"Drag-and-drop onto a cloud location in the UI but the job router picked the local move strategy; destination URI built from the peer device's display string; destination constructed with a stale device slug after re-pairing.","solutions":["Gate strategy selection on both ends: `if source.is_local() && destination.is_local()` before choosing LocalMoveStrategy.","For cross-device destinations, use the cross-device transfer path (destination carries the peer's device_slug, execute_push handles it).","Construct destination paths via the same helper that stamps the current device slug, never from raw display URIs of other devices."],"exampleFix":"// before\nif source.is_local() {\n    LocalMoveStrategy.execute(ctx, source, destination, verify, cb).await\n}\n\n// after\nif source.is_local() && destination.is_local() {\n    LocalMoveStrategy.execute(ctx, source, destination, verify, cb).await\n} else {\n    CrossDeviceCopyStrategy.execute(ctx, source, destination, verify, cb).await\n}","handlingStrategy":"type-guard","validationCode":"// both ends must be local for a rename-based move\nif !(source.is_local() && destination.is_local()) {\n    anyhow::bail!(\"local move requires local source and destination\");\n}","typeGuard":"fn is_local_move_candidate(source: &SdPath, destination: &SdPath) -> bool {\n    source.is_local() && destination.is_local()\n}","tryCatchPattern":"// re-route on the 'not local' signal rather than aborting the user's move\nlet result = LocalMoveStrategy.execute(ctx, source, dest, verify, cb).await;\nif result.as_ref().err().map(|e| e.to_string().contains(\"not local\")).unwrap_or(false) {\n    return CrossDeviceCopyStrategy.execute(ctx, source, dest, verify, cb).await;\n}\nresult","preventionTips":["Centralize strategy selection in one router that switches on SdPath kind for both ends.","Never construct destination paths from another device's display URI.","Cover all SdPath variant pairs in router unit tests."],"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"}