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
Thrown by build_legacy_migration_audit_report after it canonicalizes the --source path. The migration audit expects source to be the root directory of a legacy workspace; if the canonicalized path exists but is not a directory (it is a regular file, socket, or symlink to a file), the audit aborts because it walks subdirectories like cron/, workspace/, skills/.
Source
Thrown at ecc2/src/main.rs:5132
if observation.pinned { "/pinned" } else { "" },
observation.entity_name
);
if let Some(session_id) = observation.session_id.as_deref() {
line.push_str(&format!(" | {}", short_session(session_id)));
}
lines.push(line);
lines.push(format!(" summary {}", observation.summary));
}
lines.join("\n")
}
fn build_legacy_migration_audit_report(source: &Path) -> Result<LegacyMigrationAuditReport> {
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 mut artifacts = Vec::new();
let scheduler_paths = collect_existing_relative_paths(
&source,
&["cron/scheduler.py", "jobs.py", "cron/jobs.json"],
);
if !scheduler_paths.is_empty() {
artifacts.push(LegacyMigrationArtifact {
category: "scheduler".to_string(),
readiness: LegacyMigrationReadiness::ReadyNow,
detected_items: scheduler_paths.len(),
source_paths: scheduler_paths,
mapping: vec![View on GitHub (pinned to 01e15490f0)
Solutions
- Pass the legacy workspace root directory to --source, not a file inside it (e.g. './old-workspace' not './old-workspace/cron/jobs.json').
- Verify with 'ls -ld <path>' that the target is a directory before running the command.
- If the workspace is packaged as a tar/zip, extract it to a directory first and point --source at the extracted directory.
- Resolve any symlinks in the path to confirm the final target is a directory.
Example fix
# before ecc2 legacy audit --source ./old-workspace/cron/jobs.json # after ecc2 legacy audit --source ./old-workspace
Defensive patterns
Strategy: validation
Validate before calling
use std::path::Path;
fn ensure_workspace_dir(source: &Path) -> anyhow::Result<()> {
let canon = source.canonicalize()
.with_context(|| format!("workspace not found: {}", source.display()))?;
if !canon.is_dir() {
anyhow::bail!("workspace source must be a directory: {}", canon.display());
}
Ok(())
} Prevention
- Always run 'ls -ld <source>' before passing it to the legacy audit command.
- Standardize on passing the workspace root, never a file inside it.
When it happens
Trigger: Invoking the legacy migration audit command with a --source that points at a file (e.g. passing 'workspace.tar' or 'cron/jobs.json' instead of the workspace root), or a path whose canonical target is a file rather than the workspace directory.
Common situations: User passes a path to a single artifact instead of the workspace root; a symlink in the path resolves to a file; the workspace was extracted from an archive into a file rather than a directory.
Related errors
- Agents directory not found: ${dirPath}
- Expected a directory: ${dirPath}
- --config-dir must exist and contain a regular sixtytwo.yaml
- ${EXECUTABLE_OVERRIDE} does not point to a readable local It
- Missing value for --write
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/8571dca2bc29c41b.
Report an issue: GitHub.