sinelaw/fresh · error · io::Error (InvalidData)
Copy action without source
Error message
Copy action without source
What it means
write_recipe_to_file fails when the recipe contains Copy actions but the recipe's src_path is None. Copy actions read bytes from the source file, so without a source path the recipe cannot be executed. This is an internally inconsistent recipe (InvalidData).
Solutions
- Set recipe.src_path to the file the buffer was loaded from before writing.
- For buffers with no source file, build an insert-only recipe (no Copy actions).
- Validate recipes: if actions contain Copy, assert src_path.is_some() at build time.
Example fix
// before
let recipe = WriteRecipe { actions: vec![RecipeAction::Copy{offset:0,len:10}], src_path: None, .. };
// after
let recipe = WriteRecipe { actions: vec![RecipeAction::Copy{offset:0,len:10}], src_path: Some(original_path.into()), .. }; Defensive patterns
Strategy: validation
Validate before calling
if recipe.actions.iter().any(|a| matches!(a, RecipeAction::Copy{..})) && recipe.src_path.is_none() {
return Err(io::Error::new(io::ErrorKind::InvalidData, "recipe has Copy actions but no src_path"));
} Try / catch
match write_recipe_to_file(&recipe, ...) {
Ok(()) => (),
Err(e) if e.to_string().contains("Copy action without source") => {
// rebuild as insert-only recipe for unsaved buffers
}
Err(e) => return Err(e),
} Prevention
- Always set src_path when building a recipe with Copy actions
- Use insert-only recipes for buffers with no backing file
- Validate recipes at construction time, not at write time
When it happens
Trigger: Calling write_recipe_to_file with a recipe built with Copy actions but never assigned src_path — e.g. an in-memory-only document saved through the copy-based path instead of the insert-only path.
Common situations: Saving a new (never-loaded-from-disk) buffer whose recipe generator wrongly emitted Copy actions; a builder that forgets set_src_path.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Buffer not found
- No file path associated with buffer
- Failed to read data at offset
- Cannot save: remote connection lost
- could not read script
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/070e81222100b8ac.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor-core/src/model/buffer/save.rs:532
out_file.write_all(&chunk)?;
offset += chunk_len as u64;
}
Ok(())
}
/// Write the recipe content to a file writer.
pub(super) fn write_recipe_to_file(
fs: &Arc<dyn FileSystem + Send + Sync>,
out_file: &mut Box<dyn FileWriter>,
recipe: &WriteRecipe,
) -> io::Result<()> {
for action in &recipe.actions {
match action {
RecipeAction::Copy { offset, len } => {
// Read from source and write to output
let src_path = recipe.src_path.as_ref().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "Copy action without source")
})?;
let data = fs.read_range(src_path, *offset, *len as usize)?;
out_file.write_all(&data)?;
}
RecipeAction::Insert { index } => {
out_file.write_all(&recipe.insert_data[*index])?;
}
}
}
Ok(())
}
/// Internal helper to create a SudoSaveRequired error.
pub(super) fn make_sudo_error(
temp_path: PathBuf,
dest_path: &Path,
original_metadata: Option<FileMetadata>,
) -> anyhow::Error {View on GitHub (pinned to 67894ca546)