Hive Hive
Sign in

fix(server): convert S3 download timeouts into retryable errors

GitHub issue · Closed

Metadata
Source
tuist/tuist #11504
Updated
Jul 5, 2026
Domains
Storage
Details

Problem

TUIST-10S: ProcessBuildWorker crashes with Oban.CrashError: ** (exit) exited in: Task.Supervised.stream(60000) ** (EXIT) time out.

ExAws.S3.download_file pulls each chunk through a Task.async_stream with a per-chunk timeout: 60_000 and the default on_timeout: :exit. When an S3 chunk stalls past 60s the stream exits rather than raising. ExAws’ own download_to/3 only rescues exceptions, not exits, so the exit escaped Storage.download_to_file/3, propagated through the worker, and Oban surfaced it as a crash.

Fix

Catch the exit at the storage boundary and return {:error, reason}. download_to_file/3’s contract is now {:ok, _} | {:error, _} — never an uncaught exit.

Both ProcessBuildWorker and ProcessXcResultWorker call download_to_file/3 with the same {:error, _} = error -> error pattern, so the timeout now flows through their existing failure handling: the job retries, and the build is marked failed_processing after the final attempt instead of crashing.

Tests (TDD)

  • New download_to_file/3 test stubs ExAws.request to exit({:timeout, {Task.Supervised, :stream, [60_000]}}) — reproduced the exact Sentry exit before the fix, passes after.
  • Added a happy-path test guarding the normal {:ok, _} return.

Verified: storage_test.exs (37 tests), process_build_worker_test.exs + process_xcresult_worker_test.exs (36 tests), mix compile --warnings-as-errors, and mix credo all green.

🤖 Generated with Claude Code

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments

No GitHub comments yet.