From 8cddf9c8d5ea9572bb7e62dcd200196c0e37048c Mon Sep 17 00:00:00 2001 From: Nico Hinderling Date: Thu, 29 Jan 2026 15:56:02 -0800 Subject: [PATCH 1/2] fix(cli): Add timeout to build upload polling loop The polling loop in `build upload` could infinite loop when the server returns a pending state (Created/Assembling) without an artifact_url. Add a 5-minute timeout and 1-second sleep between iterations, matching the pattern used in the existing poll_assemble function. Fixes #2942 Co-Authored-By: Claude --- src/commands/build/upload.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/commands/build/upload.rs b/src/commands/build/upload.rs index 7d5c3a284a..96701b74e5 100644 --- a/src/commands/build/upload.rs +++ b/src/commands/build/upload.rs @@ -1,6 +1,8 @@ use std::borrow::Cow; use std::io::Write as _; use std::path::Path; +use std::thread; +use std::time::Instant; use anyhow::{anyhow, bail, Context as _, Result}; use clap::{Arg, ArgAction, ArgMatches, Command}; @@ -13,13 +15,14 @@ use zip::{DateTime, ZipWriter}; use crate::api::{Api, AuthenticatedApi, ChunkedBuildRequest, ChunkedFileState, VcsInfo}; use crate::config::Config; +use crate::constants::DEFAULT_MAX_WAIT; use crate::utils::args::ArgExt as _; #[cfg(all(target_os = "macos", target_arch = "aarch64"))] use crate::utils::build::{handle_asset_catalogs, ipa_to_xcarchive, is_apple_app, is_ipa_file}; use crate::utils::build::{ is_aab_file, is_apk_file, is_zip_file, normalize_directory, write_version_metadata, }; -use crate::utils::chunks::{upload_chunks, Chunk}; +use crate::utils::chunks::{upload_chunks, Chunk, ASSEMBLE_POLL_INTERVAL}; use crate::utils::ci::is_ci; use crate::utils::fs::get_sha1_checksums; use crate::utils::fs::TempDir; @@ -654,7 +657,9 @@ fn upload_file( // iteration of the loop) we get: // n. state=error, artifact_url unset - let result = loop { + let assemble_start = Instant::now(); + + loop { let response = api.assemble_build( org, project, @@ -685,15 +690,23 @@ fn upload_file( } if let Some(artifact_url) = response.artifact_url { - break Ok(artifact_url); + return Ok(artifact_url); } if response.state.is_finished() { bail!("File upload is_finished() but did not succeeded or error"); } - }; - result + // Check for timeout to prevent infinite loop + if assemble_start.elapsed() > DEFAULT_MAX_WAIT { + bail!( + "Timeout waiting for build assembly after {} seconds", + DEFAULT_MAX_WAIT.as_secs() + ); + } + + thread::sleep(ASSEMBLE_POLL_INTERVAL); + } } /// Utility function to parse a SHA1 digest, allowing empty strings. From 7d2383fc5abf2a1e61b2b5f7945bb07b92dcb2a3 Mon Sep 17 00:00:00 2001 From: Nico Hinderling Date: Thu, 29 Jan 2026 16:00:49 -0800 Subject: [PATCH 2/2] docs: Add changelog entry for build upload timeout fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb88997e6b..8b6f187fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes - Fixed a bug where the `dart-symbol-map` command did not accept the `--url` argument ([#3108](https://github.com/getsentry/sentry-cli/pull/3108)). +- Add timeout to `build upload` polling loop to prevent infinite loop when server returns unexpected state ([#3118](https://github.com/getsentry/sentry-cli/pull/3118)). ## 3.1.0