aaif-goose/goose · error

Failed to receive authorization code

Error message

Failed to receive authorization code

What it means

Tetrate signup waits on a channel for the OAuth authorization code delivered by the local callback server. Ok(Err(_)) means the channel closed without a code: the server task (or its sender) went away before the callback arrived. The timeout case produces a separate 'Authentication timeout' error instead.

Source

Thrown at crates/goose/src/config/signup_tetrate/mod.rs:141

                eprintln!("Server error: {}", e);
            }
        });

        let auth_url = self.get_auth_url(port);

        println!("Opening browser for Tetrate Agent Router Service authentication...");
        eprintln!("Auth URL: {}", auth_url);

        if let Err(e) = webbrowser::open(&auth_url) {
            eprintln!("Failed to open browser automatically: {}", e);
            println!("Please open this URL manually: {}", auth_url);
        }

        println!("Waiting for authentication callback on port {}...", port);

        let code = match timeout(AUTH_TIMEOUT, code_rx).await {
            Ok(Ok(code)) => Ok(code),
            Ok(Err(_)) => Err(anyhow!("Failed to receive authorization code")),
            Err(_) => Err(anyhow!("Authentication timeout - please try again")),
        }?;

        println!("Authorization code received. Exchanging for API key...");
        eprintln!("Received code: {}", code);

        let api_key = self.exchange_code(code).await?;

        // Shutdown the server if it's still running
        if let Some(tx) = self.server_shutdown_tx.take() {
            let _ = tx.send(());
        }

        Ok(api_key)
    }
}

pub use self::PkceAuthFlow as TetrateAuth;

View on GitHub (pinned to 3810898a74)

Solutions

  1. Check stderr for the server failure line; free the callback port (kill the stale/parallel flow) and rerun
  2. Run only one Tetrate signup flow at a time
  3. Retry the flow — a clean restart re-creates the listener
Defensive patterns

Strategy: retry

Validate before calling

// Before the flow, ensure the callback port is bindable:
if std::net::TcpListener::bind(("127.0.0.1", port)).is_err() {
    // report the port conflict instead of letting the server task die silently
}

Try / catch

let code = match wait_for_code().await {
    Err(e) if e.to_string() == "Failed to receive authorization code" => {
        // server dropped the channel: free the port / stop the parallel flow, then restart
        retry_flow().await?
    }
    Err(e) if e.to_string().starts_with("Authentication timeout") => {
        // user never finished the browser step; restart and guide them
        retry_flow().await?
    }
    other => other?,
};

Prevention

When it happens

Trigger: The callback server fails to bind its port (occupied by another process) or otherwise exits early, dropping the sender; the browser then has nowhere to deliver the code. Root-cause output appears on stderr.

Common situations: Concurrent signup flows competing for the callback port; a stale process holding the port; restricted environments where the local listener cannot start.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/51ec55adc6993e73. Report an issue: GitHub.