Hmbown/CodeWhale · error
bundle carries [global] entries; importing them into a…
Error message
bundle carries [global] entries; importing them into a project document would leak machine state
What it means
prepare_import refuses to import a bundle whose Project scope carries [global] entries. Importing machine-specific global configuration (providers, credentials, store paths) into a workspace-scoped project document would leak state from one machine into a shared/committed file. The library throws this as a guard before any plan is built or written.
Solutions
- Import without --project so the [global] entries land in the global document, as the message suggests.
- Edit the bundle file to remove the [global] section (or split it into a separate global bundle) before importing with --project.
- Re-export with `config export --portable --project` so only project-scoped entries are in the bundle.
Example fix
// before codewhale config import team-bundle.toml --project // after (global entries handled separately) codewhale config import global-part.toml codewhale config import project-part.toml --project
Defensive patterns
Strategy: validation
Validate before calling
let text = std::fs::read_to_string(&bundle_path)?;
let bundle: toml::Table = toml::from_str(&text)?;
let has_global = bundle.get("global").map(|t| t.as_table().map(|t| !t.is_empty()).unwrap_or(false)).unwrap_or(false);
if has_global && args.project { eprintln!("bundle has [global] entries; drop --project or split the bundle"); }
Type guard
fn importable_as_project(bundle: &Bundle) -> bool { bundle.global.entries.is_empty() } Try / catch
match prepare_import(&bundle, &store, BundleScope::Project) {
Err(e) if e.to_string().contains("leak machine state") => eprintln!("re-run without --project"),
other => other?,
} Prevention
- Export bundles per scope with --project so sections never mix.
- Lint bundles for a non-empty [global] table before scripted --project imports.
- Never commit bundles containing [global] entries to shared repos.
When it happens
Trigger: Calling apply_bundle/run_import (or the dry-run path via prepare_import) with scope=BundleScope::Project while bundle.global.entries is non-empty; typically the bundle was exported as Global or contains both sections and the user passed --project.
Common situations: Sharing a bundle exported on a personal machine into a team repo; exporting with mixed sections and importing with --project; scripting `config import --project` against a bundle fetched from a remote source that includes [global] entries.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- refusing to import non-portable config path
- import cancelled; no configuration was changed
- import refused: non-interactive use requires explicit --yes…
- MCP import preview failed
- a CNB access token is not configured in the Codewhale…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/1c5e55ec04da9228.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/config_bundles.rs:1149
Ok(()) => Ok(()),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(error) => Err(error)
.with_context(|| format!("removing newly-created config {}", target.display())),
}
}
/// Build the exact candidate used for both preview and commit. Legacy route
/// migration remains in memory until the existing ConfigStore CAS save.
fn prepare_import(
bundle: &PortableBundle,
store: &codewhale_config::ConfigStore,
scope: BundleScope,
) -> Result<PreparedImport> {
match scope {
BundleScope::Global if !bundle.project.entries.is_empty() => bail!(
"bundle carries [project] entries; import it with --project from the workspace instead"
),
BundleScope::Project if !bundle.global.entries.is_empty() => bail!(
"bundle carries [global] entries; importing them into a project document would leak machine state"
),
_ => {}
}
validate_scope_target(scope, store.path())?;
let mut plan = plan_import(bundle, &store.config, scope);
if !plan.conflicting.is_empty() || (plan.is_no_op() && plan.skipped.is_empty()) {
return Ok(PreparedImport {
plan,
candidate: store.config.clone(),
});
}
let rendered;
let original = if let Some(original) = store.original_body() {
original
} else {
rendered = store.rendered_body()?;
&renderedView on GitHub (pinned to 73e0f67d83)