charmbracelet/crush · error
mcp '%s' already has an authentication in progress
Error message
mcp '%s' already has an authentication in progress
What it means
BeginAuth enforces one in-flight browser-suppressed auth flow per server using a TryLock on a per-name lock. If a flow is already running for that server, TryLock fails and the function returns this error rather than queuing a second concurrent flow.
Source
Thrown at internal/agent/tools/mcp/init.go:437
// surfacing the authorization URL (via [MCPAuthURL]) to the user. It returns
// a finish function that must be called exactly once with the request
// context: finish blocks until the flow completes and returns the result.
//
// Only one browser-suppressed flow per server may be in progress. The
// returned cancel function aborts the flow without waiting; use it when the
// caller's context is cancelled.
func BeginAuth(cfg *config.ConfigStore, name string) (finish func(ctx context.Context) error, cancel context.CancelFunc, err error) {
m, exists := cfg.Config().MCP[name]
if !exists {
return nil, nil, fmt.Errorf("mcp '%s' not found in configuration", name)
}
if !m.OAuth || m.Type != config.MCPHttp {
return nil, nil, fmt.Errorf("mcp '%s' does not use OAuth authentication", name)
}
lock := suppressLock(name)
if !lock.TryLock() {
return nil, nil, fmt.Errorf("mcp '%s' already has an authentication in progress", name)
}
flowCtx, flowCancel := context.WithCancel(context.Background())
flowCtx = mcpoauth.WithInteractive(flowCtx)
flowCtx = context.WithValue(flowCtx, suppressBrowserKey{}, true)
finish = func(ctx context.Context) error {
defer lock.Unlock()
defer flowCancel()
done := make(chan error, 1)
go func() {
done <- runAuthFlow(flowCtx, cfg, name, m)
}()
select {
case err := <-done:
return errView on GitHub (pinned to 7944b8e522)
Solutions
- Wait for the in-flight auth flow to complete before starting another for the same server.
- Call the flow's cancel function to abort a stuck flow, then retry BeginAuth.
- De-duplicate callers: have MCPAuthenticate check whether a flow is already pending before calling BeginAuth.
- If a leaked lock persists after a crash, restart the process to reset in-memory locks.
Example fix
// before
finish, cancel, err := mcp.BeginAuth(cfg, "github") // concurrent call
// after
finish, cancel, err := mcp.BeginAuth(cfg, "github")
if err != nil && strings.Contains(err.Error(), "already has an authentication in progress") {
return nil // flow already running; wait for it
} Defensive patterns
Strategy: validation
Validate before calling
// check state before starting a new flow
if state := mcp.GetState(name); state == mcp.StateNeedsAuth && authInFlight[name] {
return nil // flow already running
} Try / catch
finish, cancel, err := mcp.BeginAuth(cfg, name)
if err != nil && strings.Contains(err.Error(), "already has an authentication in progress") {
// wait for existing flow instead of starting a duplicate
return waitForExistingAuth(ctx, name)
} Prevention
- Serialize auth attempts per server in caller code (single-flight).
- Always invoke cancel to abort and release a stuck flow.
- Debounce retry logic so it doesn't race user-initiated auth.
- Restart the process if an in-memory lock leaks after a crash.
When it happens
Trigger: Calling BeginAuth for a server whose suppressLock is already held — e.g. MCPAuthenticate deferring auth while a startup retry also began auth, or two concurrent MCPAuthenticate invocations for the same server; the previous flow's cancel/finish was never called.
Common situations: Duplicate auth attempts fired by retry logic and user command simultaneously; a crashed flow leaked the lock; tests running concurrent BeginAuth calls for the same name.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- mcp '%s' does not use OAuth authentication
- failed to get MCP pending auth: %w
- file lock is held by another process
- interactive OAuth authorization required
- failed to start OAuth callback listener: all candidate ports
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/bc481cbc6358b2c5.
Report an issue: GitHub.