nikivdev/code · error
cycle detected while loading config includes: {}
Error message
cycle detected while loading config includes: {} What it means
Thrown while loading a config file and its `include` chain when a file about to be loaded has already been visited in the current load (detected via canonicalized paths). This prevents infinite recursion, e.g. a config that includes itself directly or through a loop of two or more files. The message names the path where the cycle was detected.
Source
Thrown at src/config.rs:2384
if secrets_path.exists() {
if let Ok(secrets) = load_secrets(&secrets_path) {
merge_secrets(cfg, secrets);
tracing::debug!(path = %secrets_path.display(), "loaded secrets file");
}
}
}
}
fn load_with_includes(
path: &Path,
visited: &mut Vec<PathBuf>,
watched_paths: &mut Vec<PathBuf>,
) -> Result<Config> {
let canonical = path
.canonicalize()
.with_context(|| format!("failed to resolve path {}", path.display()))?;
if visited.contains(&canonical) {
anyhow::bail!(
"cycle detected while loading config includes: {}",
path.display()
);
}
visited.push(canonical.clone());
watched_paths.push(canonical.clone());
let contents = fs::read_to_string(&canonical)
.with_context(|| format!("failed to read flow config at {}", path.display()))?;
let mut cfg: Config = match toml::from_str(&contents) {
Ok(cfg) => cfg,
Err(err) => {
let fix = fixup::fix_toml_content(&contents);
if fix.fixes_applied.is_empty() {
return Err(err)
.with_context(|| format!("failed to parse flow config at {}", path.display()));
}
let fixed = fixup::apply_fixes_to_content(&contents, &fix.fixes_applied);View on GitHub (pinned to a747e741ae)
Solutions
- Trace the include chain printed in the message and remove the include that closes the loop
- Check for symlinks: `readlink -f <path>` to see the canonical target and de-duplicate includes
- Ensure each config includes at most one chain without returning to an ancestor
- Restructure shared settings into a base config included once by each side
Example fix
# before (cycle) # config.toml include = ["config.toml"] # after include = []
Defensive patterns
Strategy: validation
Validate before calling
// detect an include cycle before loading
fn would_cycle(path: &Path, visited: &HashSet<PathBuf>) -> Result<()> {
let canonical = path.canonicalize()?;
if visited.contains(&canonical) {
bail!("config include cycle at {}", path.display());
}
Ok(())
} Try / catch
match load_config_with_includes(&path) {
Err(e) if e.to_string().contains("cycle detected while loading config includes") => {
eprintln!("{e:#}");
eprintln!("Fix your config: remove the include that points back to an already-loaded file (check symlinks with readlink -f).");
Config::default()
}
other => other?,
} Prevention
- Draw out your include graph when editing config includes by hand
- Use `readlink -f` to check whether includes resolve to the same canonical file via symlinks
- Keep shared settings in a single base file included once
- Add a unit test that loads the shipped default config to catch cycles early
When it happens
Trigger: Config A includes config B and B includes A; a config includes itself; or two paths that canonicalize to the same file (symlink/./.. tricks) are included repeatedly.
Common situations: Hand-edited include chains that loop, symlinked config files (e.g. dotfile managers linking config into itself), copying a template that includes its own path, or directory+file includes resolving to the same canonical file.
Related errors
- cycle detected while loading config includes: {}
- No profile selected.
- expected [{}] to be a table in global flow config
- empty resolver command for {}
- FLOW_CODEX_MAPLE_LOCAL_ENDPOINT and FLOW_CODEX_MAPLE_LOCAL_I
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/ada9a0ff5995462a.
Report an issue: GitHub.