jdx/mise · error · eyre::Report
cannot prompt for bootstrap secret '{}' without an interacti
Error message
cannot prompt for bootstrap secret '{}' without an interactive terminal What it means
When a declared secret's environment variable is missing or empty, mise falls back to prompting for the value on stderr. prompt_value first checks console::user_attended_stderr(); with no interactive terminal it refuses to prompt rather than hang, because the password Input read cannot work headless.
Source
Thrown at src/system/secrets.rs:339
.next()
.is_some_and(|first| first == '_' || first.is_ascii_alphabetic())
&& characters.all(|character| character == '_' || character.is_ascii_alphanumeric())
}
fn env_state(declaration: &SecretDeclaration) -> SecretState {
match std::env::var_os(&declaration.env) {
None => SecretState::Missing,
Some(value) => match value.into_string() {
Err(_) => SecretState::InvalidUnicode,
Ok(value) if value.is_empty() && !declaration.allow_empty => SecretState::Empty,
Ok(_) => SecretState::Available,
},
}
}
fn prompt_value(declaration: &SecretDeclaration) -> Result<String> {
if !console::user_attended_stderr() {
bail!(
"cannot prompt for bootstrap secret '{}' without an interactive terminal",
declaration.name
);
}
let prompt = declaration
.description
.clone()
.unwrap_or_else(|| format!("Enter bootstrap secret {}", declaration.name));
Ok(Input::new(&prompt)
.password(true)
.theme(&crate::ui::theme::get_theme())
.run()?)
}
#[cfg(test)]
mod tests {
use super::*;
View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Export the secret's environment variable before running: CACHE_TOKEN=... mise bootstrap apply
- Run the command from an interactive terminal so the prompt can appear
- Set allow_empty = true in the secret declaration if an empty value is acceptable for this environment
Example fix
# before (CI job, no tty) mise bootstrap apply # bails: cannot prompt for bootstrap secret 'token' # after env CACHE_TOKEN="$CI_CACHE_TOKEN" mise bootstrap apply
Defensive patterns
Strategy: fallback
Validate before calling
// Before any bootstrap step that needs secrets, ensure each is resolvable headless
for declaration in declarations {
let set = std::env::var_os(&declaration.env).is_some_and(|v| !v.is_empty() || declaration.allow_empty);
if !set && !console::user_attended_stderr() {
bail!("secret '{}' has no value and no terminal is available to prompt", declaration.name);
}
} Try / catch
match result {
Err(err) if err.to_string().contains("without an interactive terminal") => {
// tell the operator which env vars to provide, then re-run with them set
}
result => result,
} Prevention
- Pre-set all declared secret env vars in CI/cron/ssh environments
- Use `ssh -t` when a one-off interactive bootstrap must prompt
- Mark optional secrets allow_empty = true so headless runs do not block on them
When it happens
Trigger: Running any bootstrap step that materializes secrets when stderr is not a TTY or the process is not user-attended: CI pipelines, cron, `ssh host mise ...` (non-interactive), piped/redirected stderr, or running under a service manager.
Common situations: Automated provisioning in CI; first run on a fresh machine over SSH without -t; scheduled/cron runs; containers where no terminal exists.
Related errors
- No tool specified and not running interactively
- managed system path '{}' is declared as both a file and a di
- invalid bootstrap secret name '{name}': use ASCII letters, d
- bootstrap secret '{name}' has invalid environment variable n
- sudo requires a password but no TTY is available. Run manual
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/f8409abc4f6cfe49.
Report an issue: GitHub.