affaan-m/ECC · error · anyhow::Error
Legacy workspace source must be a directory
Error message
Legacy workspace source must be a directory: {} What it means
The legacy schedule import canonicalizes the source workspace path and then explicitly checks `is_dir()`. If the canonicalized path resolves to a file rather than a directory, it bails with this message including the path. The importer walks the directory tree for schedule drafts, so a directory is required.
Solutions
- Pass the legacy workspace root directory, not a file, as the source argument.
- Verify with `ls -d <path>` that the target is a directory after resolving symlinks.
- If you intended to import a single jobs file, use the jobs-file option/command instead of the workspace import.
Example fix
// before
import_legacy_schedules(Path::new("~/old-ws/jobs.json"))?;
// after
import_legacy_schedules(Path::new("~/old-ws"))?; Defensive patterns
Strategy: validation
Validate before calling
// Rust
let source = Path::new(raw);
if !source.is_dir() {
anyhow::bail!("source must be a directory: {}", source.display());
} Type guard
fn is_directory(p: &Path) -> bool {
p.is_dir()
} Try / catch
match import_legacy_schedules(&source) {
Err(e) if e.to_string().contains("must be a directory") => {
eprintln!("Pass the workspace root directory, not a file: {}", source.display());
}
other => other?,
} Prevention
- Keep file-based imports (single jobs file) and directory-based imports on separate, clearly named commands/flags.
- Resolve symlinks before passing paths to migration commands.
- Document that workspace arguments must be directories in the command help text.
When it happens
Trigger: Passing a file path (e.g. a single jobs JSON file or an archive) to the legacy schedule import where a workspace directory is expected; a symlink resolving to a file after canonicalization.
Common situations: Confusing the schedule-import command with the jobs-file option (file) vs the workspace argument (directory); pointing at `config.json` inside the legacy workspace instead of the workspace root; a broken symlink chain that canonicalizes to a file.
Related errors
- Legacy workspace not found
- legacy scheduler jobs file must be a JSON object or array
- agent profile inheritance cycle
- all overlays must be readable local files
- all takes must be readable local files
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/8571dca2bc29c41b.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/main.rs:6018
parts.push(format!("--project {}", shell_quote_double(project)));
}
if let Some(task_group) = draft.task_group.as_deref() {
parts.push(format!("--task-group {}", shell_quote_double(task_group)));
}
Some(parts.join(" "))
}
fn import_legacy_schedules(
db: &session::store::StateStore,
cfg: &config::Config,
source: &Path,
dry_run: bool,
) -> Result<LegacyScheduleImportReport> {
let source = source
.canonicalize()
.with_context(|| format!("Legacy workspace not found: {}", source.display()))?;
if !source.is_dir() {
anyhow::bail!(
"Legacy workspace source must be a directory: {}",
source.display()
);
}
let drafts = load_legacy_schedule_drafts(&source)?;
let source_path = source.join("cron/jobs.json");
let source_path = source_path
.strip_prefix(&source)
.unwrap_or(&source_path)
.display()
.to_string();
let mut report = LegacyScheduleImportReport {
source: source.display().to_string(),
source_path,
dry_run,
jobs_detected: drafts.len(),View on GitHub (pinned to 8321021c54)