Skip to content

fix: wire upload timeout_secs to HTTP read_timeout (fixes 499 on 50GB+ uploads)#53

Merged
Gumbees merged 1 commit into
developmentfrom
fix/upload-timeout-wire
May 26, 2026
Merged

fix: wire upload timeout_secs to HTTP read_timeout (fixes 499 on 50GB+ uploads)#53
Gumbees merged 1 commit into
developmentfrom
fix/upload-timeout-wire

Conversation

@Gumbees

@Gumbees Gumbees commented May 26, 2026

Copy link
Copy Markdown
Collaborator

Root cause

ImmichClient::with_bandwidth_limit hardcoded .read_timeout(Duration::from_secs(120)) and never read the configured UploadConfig.timeout_secs. The read_timeout is an inactivity timeout -- it fires when no response bytes arrive for the configured duration. On a slow backend (Orange Pi / Rockchip SBC), Immich can spend several minutes checksumming a 50 GB file after receiving the last upload byte; no response bytes flow during that window. After 120 seconds of silence, reqwest fires the timeout and drops the connection -- the server sees a premature client close and returns HTTP 499. The timeout_secs config knob (intended to control this) was dead-wired: set in config, never passed to the client.

Fix

  • src/api/mod.rs: added timeout_secs: u64 param to with_bandwidth_limit; .read_timeout now uses it instead of the hardcoded 120s. ImmichClient::new (connection-test path used by settings/first-run UI) defaults to 3600s. Updated the comment block to explain that this is an inactivity-only timeout, deliberately NOT an overall timeout (a healthy 50 GB transfer at 10 MB/s takes ~83 min; an overall timeout would kill it).
  • src/main.rs: passes config.upload.timeout_secs to with_bandwidth_limit at app startup.
  • src/app.rs: apply_new_config switched from ImmichClient::new to with_bandwidth_limit, so both bandwidth limit and inactivity timeout track the live config when server settings change.
  • src/config.rs: UploadConfig::default().timeout_secs raised from 300 to 3600 (1 hour); doc comment updated to describe inactivity-only semantics and the slow-backend use case.

No overall .timeout() was added -- the existing design deliberately omits it and that is preserved.

Users can raise timeout_secs in settings if their server needs more than an hour to process a single file.

Born-green gate

cargo test --locked         → test result: ok. 110 passed; 0 failed
cargo check --release --locked → Finished `release` profile
cargo clippy --all-targets -- -D warnings → Finished `dev` profile (zero warnings)
cargo fmt --all -- --check  → (clean, no output)

All four CI gates pass locally.

… on large uploads)

The HTTP read_timeout in ImmichClient was hardcoded to 120s and never
read the configured UploadConfig.timeout_secs. On a slow backend (Orange
Pi / Rockchip SBC), Immich can spend several minutes checksumming a 50GB
file after the last upload byte is sent; no response bytes arrive during
that window, so the 120s inactivity timeout fires and the client aborts
with HTTP 499 (client closed request).

Fix:
- Add timeout_secs: u64 param to with_bandwidth_limit; use it as
  .read_timeout(Duration::from_secs(timeout_secs)) instead of 120.
- ImmichClient::new defaults to 3600s (same as the new config default).
- src/main.rs: passes config.upload.timeout_secs to with_bandwidth_limit.
- src/app.rs: apply_new_config now calls with_bandwidth_limit (was ::new)
  so bandwidth limit AND timeout both track the live config on server change.
- src/config.rs: UploadConfig::default().timeout_secs 300 -> 3600 (1 hour);
  doc comment updated to describe inactivity-only semantics and why 1 hour
  is appropriate for 50GB+ uploads to slow servers.

No overall .timeout() added -- a 50GB upload at 10MB/s legitimately
streams for ~83 minutes; an overall timeout would kill healthy transfers.
Only the inactivity (read) timeout is configurable. Users can raise
timeout_secs in settings if their server is slower still.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Gumbees Gumbees merged commit ccfdb73 into development May 26, 2026
2 checks passed
@Gumbees Gumbees deleted the fix/upload-timeout-wire branch May 26, 2026 04:41
Gumbees added a commit that referenced this pull request May 26, 2026
#53 (wire upload timeout_secs through to read_timeout, fixes 499s on large
uploads) landed on development without a version bump — and 0.2.2 was already
released at the prior tag. Bump to 0.2.3 so the dev->release push tags a fresh
v0.2.3 and ships the fixed binary.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant