gitbutlerapp/gitbutler · error

Username ambiguous, got

Error message

Username ambiguous, got {accounts_to_delete:?}

What it means

`but config forge forget` matches the given username against known forge accounts. If the pattern matches multiple accounts, the CLI would normally prompt the user to pick one interactively; but if the output channel cannot provide terminal input (piped stdout, non-TTY, or embedded usage), it cannot disambiguate and bails, printing the ambiguous account list.

Solutions

  1. Pass the exact, full username of the single account to forget so the match is unambiguous
  2. Run the command in an interactive terminal so the selection prompt can be shown
  3. List accounts first with `but config forge list-users` to find the exact username

Example fix

// before
but config forge forget john | tee log   # matches john@a and john@b
// after
but config forge forget john@acmecorp
Defensive patterns

Strategy: validation

Validate before calling

async function resolveUniqueAccount(pattern) {
  const users = (await exec('but config forge list-users')).stdout.split('\n').filter(Boolean);
  const matches = users.filter(u => u.includes(pattern));
  if (matches.length !== 1) throw new Error(`Pattern matches ${matches.length} accounts: ${matches}`);
  return matches[0];
}

Type guard

const isUniqueMatch = (matches) => Array.isArray(matches) && matches.length === 1;

Try / catch

try {
  await exec(`but config forge forget ${exactUsername}`);
} catch (e) {
  if (e.message.includes('Username ambiguous')) {
    console.error('Pass the exact username; see list-users output in the error');
  } else throw e;
}

Prevention

When it happens

Trigger: Running `but config forge forget <pattern>` where the name/pattern matches two or more stored forge accounts AND stdout is not an interactive terminal (prepare_for_terminal_input returns None), e.g. in scripts or CI.

Common situations: Two GitHub accounts stored (work + personal) with a partial-name pattern matching both; running the forget command from a script with redirected output; CI pipelines cleaning up forge credentials.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/0b7ed4b67896d199. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/command/config.rs:1308

        if let Some((username, out)) = username.zip(out.for_human()) {
            writeln!(out, "No known forge account matching '{username}'")?;
        }
        return Ok(());
    }

    // Handle different scenarios based on number of accounts
    match accounts_to_delete.as_slice() {
        [single_account] => {
            // Single account: delete automatically
            forget_account(single_account)?;
            if let Some(out) = out.for_human() {
                writeln!(out, "Forgot forge account '{single_account}'")?;
            }
        }
        _ => {
            // Multiple accounts: prompt user to select
            let Some(mut input) = out.prepare_for_terminal_input() else {
                anyhow::bail!("Username ambiguous, got {accounts_to_delete:?}");
            };
            let account_options = accounts_to_delete
                .into_iter()
                .map(|account| (account.to_string(), account))
                .collect::<Vec<_>>();
            let account_options = nonempty::NonEmpty::from_vec(account_options)
                .context("No accounts available to forget")?;

            let selected_accounts = input
                .prompt_multi_select(
                    "Which of the following accounts do you want to forget?",
                    &account_options,
                )?
                .ok_or_else(|| anyhow::anyhow!("Could not determine which accounts to delete"))?;

            if selected_accounts.is_empty() {
                writeln!(input, "No accounts were selected to forget.")?;
                return Ok(());

View on GitHub (pinned to 58e5313667)