nikivdev/code · error
repos root is immutable; use
Error message
repos root is immutable; use {} or {} or set {}=1 to override What it means
The repos-root guard in src/repos.rs refuses to operate on a repository root other than the two built-in defaults (DEFAULT_REPOS_ROOT or DEFAULT_CODE_ROOT) unless an explicit override is enabled. This protects users from accidentally cloning, syncing, or mutating repos in an arbitrary directory that may not be managed by this tool.
Solutions
- Use the default repos root or default code root instead of a custom path
- Set the REPOS_ROOT_OVERRIDE_ENV environment variable to 1 to explicitly allow a non-default root
- Verify the path is correctly expanded (config::expand_path) and matches one of the two allowed defaults
Example fix
// before
repos_cmd(root: PathBuf::from("/tmp/my-repos"), ...)
// after
export REPOS_ROOT_OVERRIDE=1 # or pass the default root
repos_cmd(root: config::expand_path(DEFAULT_REPOS_ROOT), ...) Defensive patterns
Strategy: validation
Validate before calling
fn is_allowed_repos_root(root: &Path) -> bool {
let d = config::expand_path(DEFAULT_REPOS_ROOT);
let c = config::expand_path(DEFAULT_CODE_ROOT);
root == d || root == c || std::env::var(REPOS_ROOT_OVERRIDE_ENV).as_deref() == Ok("1")
}
if !is_allowed_repos_root(&root) { eprintln!("root not allowed"); return; } Try / catch
match do_repos_op(&root) {
Err(e) if e.to_string().contains("repos root is immutable") => eprintln!("pass the default root or set the override env var"),
other => other?,
} Prevention
- Always derive the root from config::expand_path(DEFAULT_*) instead of hardcoding paths
- Set REPOS_ROOT_OVERRIDE_ENV=1 only in throwaway/dev environments
- Document the allowed roots in your CI scripts
When it happens
Trigger: Calling a repos command with a custom root path argument that matches neither config::expand_path(DEFAULT_REPOS_ROOT) nor config::expand_path(DEFAULT_CODE_ROOT) while the REPOS_ROOT_OVERRIDE_ENV environment variable is unset.
Common situations: Developers passing --root or a positional path pointing at a scratch/personal directory; CI systems overriding the root without also setting the override env var; expansions of '~' differing from the configured default path.
Related errors
- agent run requires a non-empty query
- AI returned unknown command
- archive message cannot be empty
- archive message must include at least one letter or number
- deploy_task ' ' not found. Available tasks
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/9f643b7fc3a84ffc.
Report an issue: GitHub.
Appendix: source
Thrown at src/repos.rs:1773
Ok(RepoRef {
owner: owner.to_string(),
repo: repo.to_string(),
})
}
pub(crate) fn normalize_root(raw: &str) -> Result<PathBuf> {
let expanded = config::expand_path(raw);
let cwd = std::env::current_dir().context("failed to resolve current directory")?;
let root = if expanded.is_absolute() {
expanded
} else {
cwd.join(expanded)
};
let default_repos_root = config::expand_path(DEFAULT_REPOS_ROOT);
let default_code_root = config::expand_path(DEFAULT_CODE_ROOT);
if root != default_repos_root && root != default_code_root && !repos_root_override_enabled() {
bail!(
"repos root is immutable; use {} or {} or set {}=1 to override",
default_repos_root.display(),
default_code_root.display(),
REPOS_ROOT_OVERRIDE_ENV
);
}
Ok(root)
}
fn repos_root_override_enabled() -> bool {
match std::env::var(REPOS_ROOT_OVERRIDE_ENV) {
Ok(value) => {
let trimmed = value.trim();
!trimmed.is_empty() && trimmed != "0"
}
Err(_) => false,
}View on GitHub (pinned to a747e741ae)