From 7772c88d7b207bc11313679a4628b9cef157a6f6 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 6 Apr 2026 13:57:18 -0400 Subject: [PATCH] Add 2-minute timeout to OAuth callback listener If the user starts the sign-in flow but never completes it in the browser, the TCP listener on port 1455 would block forever. Race the accept against a 2-minute timer so the port is released and the UI gets a meaningful error. --- .../src/provider/openai_subscribed.rs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/crates/language_models/src/provider/openai_subscribed.rs b/crates/language_models/src/provider/openai_subscribed.rs index 5ce6b44b2bcb9c98ddf59c41242fb40d2b7b4f69..4a6d9cc9fef8da34349d56ff1d9bab8d427626cc 100644 --- a/crates/language_models/src/provider/openai_subscribed.rs +++ b/crates/language_models/src/provider/openai_subscribed.rs @@ -2,7 +2,7 @@ use anyhow::{Context as _, Result, anyhow}; use base64::Engine as _; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use credentials_provider::CredentialsProvider; -use futures::{FutureExt, StreamExt, future::BoxFuture, future::Shared}; +use futures::{FutureExt, StreamExt, future::BoxFuture, future::Either, future::Shared}; use gpui::{AnyView, App, AsyncApp, Context, Entity, SharedString, Task, Window}; use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest}; use language_model::{ @@ -17,7 +17,7 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use smol::io::{AsyncReadExt as _, AsyncWriteExt as _}; use std::sync::Arc; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use ui::{ConfiguredApiCard, prelude::*}; use util::ResultExt as _; @@ -690,7 +690,22 @@ async fn await_oauth_callback(expected_state: &str) -> Result { .await .context("Failed to bind to port 1455 for OAuth callback. Another application may be using this port.")?; - let (mut stream, _) = listener.accept().await?; + let accept_future = listener.accept(); + let timeout_future = smol::Timer::after(Duration::from_secs(120)); + + let (mut stream, _) = match futures::future::select( + std::pin::pin!(accept_future), + std::pin::pin!(timeout_future), + ) + .await + { + Either::Left((result, _)) => result?, + Either::Right((_, _)) => { + return Err(anyhow!( + "OAuth sign-in timed out after 2 minutes. Please try again." + )); + } + }; let mut buffer = vec![0u8; 4096]; let n = stream.read(&mut buffer).await?;