spacedriveapp/spacedrive · error · anyhow::Error
Failed to build action: {}
Error message
Failed to build action: {} What it means
Before executing a copy, the CLI rebuilds the action via `FileCopyAction::from_input(input)`, which delegates to the core builder's `build()` (core/src/ops/files/copy/action.rs:279) and fails on invalid builder state. The CLI wraps that failure string in 'Failed to build action: {}'. The text after the colon is the underlying builder error and names the real problem, e.g. missing or empty sources/destination.
Source
Thrown at apps/cli/src/domains/file/mod.rs:120
}
);
}
}
Ok(())
}
/// Run file copy with confirmation handling
async fn run_copy_with_confirmation(
ctx: &Context,
mut input: sd_core::ops::files::copy::input::FileCopyInput,
) -> Result<JobId> {
use crate::util::confirm::prompt_for_choice;
use sd_core::infra::action::LibraryAction;
use sd_core::ops::files::copy::action::FileCopyAction;
// Build the action from input for validation purposes
let action = FileCopyAction::from_input(input.clone())
.map_err(|e| anyhow::anyhow!("Failed to build action: {}", e))?;
// Use the action's validation method to check for conflicts
// For CLI validation, we'll use a simplified approach since we don't have full library context
// In a production system, you'd want to pass the actual library context
// Simple conflict detection - check if destination exists and overwrite is not enabled
if !input.overwrite {
let has_conflict = check_for_simple_conflicts(&action).await?;
if has_conflict {
use sd_core::infra::action::ConfirmationRequest;
let request = ConfirmationRequest {
message: "Destination file(s) already exist. What would you like to do?"
.to_string(),
choices: vec![
"Overwrite the existing file(s)".to_string(),
"Rename the new file(s) (e.g., file.txt -> file (1).txt)".to_string(),
"Abort this copy operation".to_string(),View on GitHub (pinned to 6dfeccf211)
Solutions
- Read the suffix after 'Failed to build action: ' — it is the exact builder message (which field was missing).
- Verify the arguments actually arrived: quote globs (`sd-cli file copy '/data/*.txt'` style handling) and echo the expanded command before running.
- Confirm the source path exists and the destination is provided on the command line.
- If the inner message mentions a field mismatch, your sd-cli build may be older than core — rebuild the workspace (`cargo build`) so CLI and core agree.
Example fix
# before cd /data && sd-cli file copy *.txt /backup # glob matched nothing -> empty sources -> Failed to build action: ... # after ls /data/*.txt >/dev/null && sd-cli file copy /data/*.txt /backup
Defensive patterns
Strategy: validation
Validate before calling
fn copy_input_valid(input: &FileCopyInput) -> bool {
!input.sources.is_empty() && input.destination.as_os_str().len() > 0
}
if !copy_input_valid(&input) { anyhow::bail!("copy needs at least one source and a destination"); } Try / catch
if let Err(e) = run_copy_with_confirmation(ctx, input).await {
let msg = e.to_string();
if msg.contains("Failed to build action") {
// the suffix after the colon is the builder's own message; surface only that
eprintln!("copy rejected: {}", msg.split_once(": ").map(|(_, r)| r).unwrap_or(&msg));
}
return Err(e);
} Prevention
- Quote globs and confirm they match something before passing them as copy sources.
- Validate that source and destination arguments are present before invoking the CLI from scripts.
- Keep sd-cli and core built from the same commit so the input struct and builder stay in sync.
When it happens
Trigger: Running `sd-cli file copy` where argument parsing produced an empty source list or an unset destination (e.g. flags consumed by a shell glob that matched nothing), or a FileCopyInput assembled programmatically with missing required fields.
Common situations: Scripts passing an unquoted glob that expands to zero files; copying where the source argument was eaten by an earlier flag; version drift between the CLI's input shape and the core builder's required fields.
Related errors
- Operation aborted by user
- Invalid choice selected
- Path is required in non-interactive mode
- Path must be a directory: {}
- Invalid event type: {}
AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16).
Data as JSON: /api/errors/e6f91f83a8c5e706.
Report an issue: GitHub.