spacedriveapp/spacedrive · warning · Error
useSpacedriveClient must be used within SpacedriveProvider
Error message
useSpacedriveClient must be used within SpacedriveProvider
What it means
Returned inside sync_now's copy-job dispatch branch when the SdPath list built from operations.to_copy is empty — the code takes source_paths.first() to derive the MVP destination and errors when there is nothing to copy. Because the branch was entered expecting copies, this indicates an empty/degenerate operation set rather than a user-input problem.
Source
Thrown at packages/ts-client/src/hooks/useClient.tsx:48
export function SpacedriveProvider({ client, children }: SpacedriveProviderProps) {
return (
<QueryClientProvider client={queryClient}>
<SpacedriveClientContext.Provider value={client}>
{children}
</SpacedriveClientContext.Provider>
</QueryClientProvider>
);
}
/**
* Hook to access the Spacedrive client
* Must be used within a SpacedriveProvider
*/
export function useSpacedriveClient(): SpacedriveClient {
const client = useContext(SpacedriveClientContext);
if (!client) {
throw new Error("useSpacedriveClient must be used within SpacedriveProvider");
}
return client;
}
// Also export for direct use
export { useClient };
function useClient() {
return useSpacedriveClient();
}
View on GitHub (pinned to 6dfeccf211)
Solutions
- Treat it as a no-op: if nothing changed, skip the copy-job dispatch instead of surfacing an error (the operations already told you to_copy is empty).
- Check the computed operation counts (copy_count/delete_count logged by sync_now) to confirm the diff is genuinely empty.
- File/track a code fix: guard `if source_paths.is_empty() { return Ok(None) }` before building FileCopyJob, since erroring on an empty batch is not actionable.
Example fix
// before
let destination = if let Some(first) = source_paths.first() {
first.clone()
} else {
return Err(anyhow::anyhow!("No paths to copy"));
};
// after (empty diff is a no-op, not an error)
let Some(first) = source_paths.first() else {
return Ok(None); // nothing to copy
};
let destination = first.clone(); Defensive patterns
Strategy: fallback
Validate before calling
// Skip dispatch when the diff has no copies.
if operations.source_to_target.to_copy.is_empty()
&& operations.target_to_source.as_ref().map_or(true, |o| o.to_copy.is_empty())
{
return Ok(None); // nothing to copy, nothing to dispatch
} Try / catch
match dispatch_copy_job(&operations).await {
Ok(h) => Ok(h),
Err(e) if e.to_string() == "No paths to copy" => Ok(None), // empty diff = no-op
Err(e) => Err(e),
} Prevention
- Check to_copy counts before entering the copy-dispatch branch.
- Treat already-in-sync conduits as success (no-op) in orchestration code.
- Track the MVP destination limitation ('first entry's parent') — verify it matches intended target semantics before relying on it.
When it happens
Trigger: A sync calculation that produced zero copy operations while still entering the dispatch branch — e.g. trees already in sync (only deletes or no-ops), or a race where the operations were computed against stale state. The comment marks it as MVP behavior: destination is 'first entry's parent', which is undefined for an empty list.
Common situations: Repeated sync_now calls on an already-converged conduit; deletion-only diffs; edge case in operation calculation returning an empty to_copy alongside a non-empty aggregate copy_count path.
Related errors
- Library with ID '${libraryId}' not found
- This operation requires an active library. Use switchToLibra
- ${isQuery ? "Query" : "Action"} failed: ${JSON.stringify(err
- Unexpected response: ${JSON.stringify(response)}
- No library selected. Use client.switchToLibrary() first.
AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16).
Data as JSON: /api/errors/03ce34c7fb482936.
Report an issue: GitHub.