gitbutlerapp/gitbutler · error
Pass either --api-key or --api-key-env, not both
Error message
Pass either --api-key or --api-key-env, not both
What it means
`resolve_secret_input` normalizes the two mutually exclusive ways of supplying an API secret non-interactively: `--api-key <value>` (inline) and `--api-key-env <var>` (read from environment). Passing both is treated as a caller mistake because the intended secret source is ambiguous, so it bails before reading either value.
Solutions
- Remove either --api-key or --api-key-env, keeping only one secret source
- Prefer --api-key-env in scripts to avoid leaking the key into shell history and process listings
- Audit wrapper scripts/aliases that may inject a default --api-key flag
Example fix
// before but config ai --provider openai --api-key sk-1 --api-key-env OPENAI_API_KEY // after but config ai --provider openai --api-key-env OPENAI_API_KEY
Defensive patterns
Strategy: validation
Validate before calling
function assertSingleSecretSource({ apiKey, apiKeyEnv }) {
const n = [apiKey, apiKeyEnv].filter(Boolean).length;
if (n > 1) throw new Error('Pass either --api-key or --api-key-env, not both');
} Type guard
const hasExactlyOneSecret = (o) => ['apiKey','apiKeyEnv'].filter(k => o[k] != null).length === 1;
Try / catch
try {
await configureAi(args);
} catch (e) {
if (e.message.includes('not both')) {
console.error('Strip one of --api-key / --api-key-env from your command template');
} else throw e;
} Prevention
- Standardize on --api-key-env in scripts; never hardcode --api-key in wrappers
- Check aliases/functions for injected default flags
- Validate flag sets before composing CLI invocations
When it happens
Trigger: Invoking `but config ai ... --api-key X --api-key-env MY_VAR` (or the SDK equivalent passing both Some values to ai_config_non_interactive).
Common situations: Shell scripts accumulating flags from templates or defaults so both options end up set; users unsure which flag to use and passing both; config generators merging CLI profiles.
Understand the failure class
Background: "mutually exclusive" flag errors: what "can't supply both nx and xx", "--raw is not compatible with -i" and "cannot be used with" mean, and how to fix them — this error's family across 29 libraries.
Related errors
- --ai cannot be combined with a resolve subcommand. For one…
- Cannot pass both --local and --global
- Cannot use both --detect and --path options together
- Cannot use both --fix-formatting and --message flags…
- Conflicted uncommitted files can only be marked as…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/9214f8529c2c3d6f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but/src/command/config.rs:1660
t.sym().success,
t.config_value.paint(provider.display_name()),
t.hint.paint(scope.as_str())
)?;
} else if let Some(out) = out.for_json() {
out.write_value(serde_json::json!({
"provider": provider.as_git_config_value(),
"scope": scope.as_str(),
}))?;
}
Ok(())
}
fn resolve_secret_input(
api_key: Option<String>,
api_key_env: Option<String>,
) -> Result<Option<Sensitive<String>>> {
if api_key.is_some() && api_key_env.is_some() {
anyhow::bail!("Pass either --api-key or --api-key-env, not both")
}
if let Some(value) = api_key {
return Ok(Some(Sensitive(value)));
}
if let Some(env_name) = api_key_env {
let value = std::env::var(&env_name)
.with_context(|| format!("Environment variable '{env_name}' is not set"))?;
return Ok(Some(Sensitive(value)));
}
Ok(None)
}
fn require_non_interactive_secret_if_byok(
key_option: AiKeyOption,
secret: Option<&Sensitive<String>>,View on GitHub (pinned to 58e5313667)