{"record":{"id":"739b651b6308f5ce","repo":"spacedriveapp/spacedrive","slug":"source-must-be-a-physical-path-for-pull-operation","errorCode":null,"errorMessage":"Source must be a physical path for PULL operation","messagePattern":"Source must be a physical path for PULL operation","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"core/src/ops/files/copy/strategy.rs","lineNumber":428,"sourceCode":"\t\t\t\terror!(\"PUSH transfer failed: {}\", e);\n\t\t\t\tctx.log(format!(\"PUSH transfer FAILED: {}\", e));\n\t\t\t\tErr(e)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Execute a PULL operation (remote -> local)\n\tasync fn execute_pull<'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_device_slug, source_path) = source\n\t\t\t.as_physical()\n\t\t\t.ok_or_else(|| anyhow::anyhow!(\"Source must be a physical path for PULL operation\"))?;\n\n\t\tlet local_dest_path = destination\n\t\t\t.as_local_path()\n\t\t\t.ok_or_else(|| anyhow::anyhow!(\"Destination must be local path for PULL operation\"))?;\n\n\t\tlet library = ctx.library();\n\t\tlet source_device_id = library\n\t\t\t.resolve_device_slug(source_device_slug)\n\t\t\t.ok_or_else(|| anyhow::anyhow!(\n\t\t\t\t\"Could not resolve source device slug '{}' to UUID in library {}. Device may not be registered in this library.\",\n\t\t\t\tsource_device_slug,\n\t\t\t\tlibrary.id()\n\t\t\t))?;\n\n\t\tdebug!(\n\t\t\t\"RemoteTransferStrategy PULL: device:{} ({}) -> {}\",\n\t\t\tsource_device_slug,\n\t\t\tsource_device_id,","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/spacedriveapp/spacedrive/blob/6dfeccf2113039e35f2ce735f945e70dc3e4ea45/core/src/ops/files/copy/strategy.rs#L410-L446","documentation":"execute_pull requires the source SdPath in physical form — a (device_slug, PathBuf) pair identifying a file on a specific remote device. as_physical() returns None when the source is a local or virtual/managed path with no device binding (strategy.rs:426-428), so the job cannot know which peer to pull from.","triggerScenarios":"PULL issued with a source constructed as a local path; source is a virtual library location without device binding; RemoteTransferStrategy invoked directly for what is actually a local-to-local copy.","commonSituations":"Custom job code that always uses the remote strategy; SdPath built from a local location record; wrong direction chosen when both paths are local.","solutions":["Represent the source as a physical path bound to the remote device's slug","If the source is local, use the local copy strategy instead of remote PULL","Fix direction determination so local sources never reach execute_pull","Validate source.as_physical().is_some() before dispatching the job"],"exampleFix":"// before\nlet source = SdPath::local(PathBuf::from(\"/mnt/data/a.jpg\")); // no device binding\nremote_strategy.execute_pull(ctx, &source, &dest).await?; // \"Source must be a physical path for PULL operation\"\n\n// after\nlet source = SdPath::physical(\"nas\", PathBuf::from(\"/export/a.jpg\"));\nremote_strategy.execute_pull(ctx, &source, &dest).await?;","handlingStrategy":"type-guard","validationCode":"if source.as_physical().is_none() {\n    return Err(anyhow::anyhow!(\"PULL requires a physical (device-scoped) source; got {}\", source));\n}","typeGuard":"fn is_physical_source(source: &SdPath) -> bool {\n    source.as_physical().is_some()\n}","tryCatchPattern":null,"preventionTips":["Bind remote sources to a device slug via SdPath::physical before pulling","Route local-to-local copies to the local strategy","Check as_physical() during path construction, not inside the strategy"],"tags":["rust","spacedrive","pull","sd-path","cross-device","copy"],"backgroundTag":null,"analyzedSha":"6dfeccf2113039e35f2ce735f945e70dc3e4ea45","analyzedAt":"2026-08-16T11:26:17.074Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}