Skip to content

Flake: sbt 1.2.8 warm-cache update in Dockerfile.sbt hangs until the 45-min job timeout #1665

Open

Description

docker build of tests/docker/Dockerfile.sbt hung in its warm-cache layer three times in about 90 minutes this morning (2026-10-22). Each time the job sat until its 45-min timeout-minutes killed it.

Run / attempt Job Runner Hung step Duration
39745825228 #1 coverage-docker (sbt) depot-5chxnxdgmt Build sbt image 08:17 → 09:02 (cancelled by timeout)
39745825228 #1 e2e-docker (sbt) depot-0xcx81brcg Build sbt image 08:17 → 09:02 (cancelled by timeout)
39745825228 #2 e2e-docker (sbt) depot-bdcmbfmvlt Build sbt image 09:02 → 45-min timeout

In the attempt-1 coverage-docker (sbt) log the sbt launcher fetches sbt 1.2.8 and Scala 2.12.7 in ~3 s. Then it prints nothing for 44 min:

#17 3.628 [info] Loading project definition from /tmp/warm-1.2.8/project
#17 3.709 [info] Updating ProjectRef(uri("file:/tmp/warm-1.2.8/project/"), "warm-1-2-8-build")...
2026-10-22T09:01:59Z ##[error]Step canceled by GitHub

The usual time for the whole job is ~6 min (nightly e2e-docker (sbt) on 10-19 and 10-21).

Impact: schedule and workflow_dispatch only (coverage-docker / e2e-docker); sbt-compatibility.yml has not run since 10-09. Not merge-blocking today. But each hang burns 45 runner-min per sbt leg and turns the nightly red with no diagnostic.

Likely cause (unconfirmed): sbt 1.2.8 resolves its build definition through Ivy, which has no socket read timeout. One stalled connection to a resolver (Maven Central, repo.scala-sbt.org, repo.typesafe.com) then hangs update forever instead of failing. The same layer's curl downloads all have --retry; this step has neither a retry nor a deadline.

Candidate fix for a future janitor run:

  • Pass JVM socket timeouts (-Dsun.net.client.defaultConnectTimeout / -Dsun.net.client.defaultReadTimeout) to the warm sbt update calls, so a stalled resolver fails fast and the layer can retry once.
  • First confirm which host stalls: run with -Dsbt.log.noformat=true -debug on a dispatch.

Generated by Claude Code

Activity

  1. mikolalysenko commented on Oct 22, 2026

    @mikolalysenko
    CollaboratorAuthor

    Two more occurrences, both in the workflow_dispatch CI run 39745825228 on ci-janitor/uv-settings-test-windows (#1663):

    • Attempt 1: e2e-docker (sbt), job 119716408680. Cancelled at the 45-minute limit (08:16:57 to 09:02:02 UTC).
    • Attempt 2: e2e-docker (sbt), job 119735730658. Cancelled the same way (09:02:36 to 09:47:38 UTC). Its last output is [launcher] getting org.scala-sbt sbt 1.2.8 and then Updating ProjectRef(uri("file:/tmp/warm-1.2.8/project/"), "warm-1-2-8-build")... at 09:29:53. Nothing more was logged until the cancel at 09:47:36, an 18-minute silent stall.

    Impact: the dispatch run's ci-ok went red on a test-only PR, and the final-review routine disarmed auto-merge on #1663. For comparison, on main's nightly with a warm image cache this job took about 6 minutes (2026-10-21, 05:52 to 05:59). The stall shows up when the image is built without a cache, as on a dispatch run on a branch.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 22, 2026

    @mikolalysenko
    CollaboratorAuthor

    CI janitor (2026-10-22 15:5x UTC): another occurrence, plus evidence that changes the likely cause.

    4th occurrence: main's nightly 39730006970, coverage-docker (sbt) (job 119664378238). The SBT_WARM_VERSIONS=1.2.8 1.13.0 slice sat on Updating ProjectRef(... "warm-1-2-8-build")... from 05:53:28 until the 45-min cancel at 06:38. So it does hit main's nightly too, not just cold dispatch builds. The same night's full-image e2e-docker (sbt) passed, and so did every sbt image build on 10-18 to 10-21 (about 3 min for the build step).

    It is probably not a no-timeout hang. The full-image build in dispatch attempt 2 (job 119735730658) warms 0.13.18 before 1.2.8, and 0.13.18 logs every Ivy Resolving line. The step was running at the normal rate, just very slowly:

    • [launcher] getting org.scala-sbt sbt 0.13.18 took 637 s (09:03:01 to 09:13:38).
    • After that, almost every Resolving <module> advanced in steps of ~20 s, 40 s, 60 s or 80 s. Examples: scala-library 2.10.7 at +40 s, scala-compiler at +40 s, oss-parent;9 at +60 s, codehaus-parent;4 at +80 s. That is a fixed per-request timeout hit once or more per module, so sockets do time out.
    • The jars themselves downloaded in 0.1 to 1.6 s each, from both repo1.maven.org and repo.typesafe.com. Its whole update finished at 1608 s.
    • Then 1.2.8 started at 1613 s. 1.x Ivy doesn't print Resolving lines, so the same slow crawl over its much larger build definition looks like silence. It was cancelled 18 min later.

    So the likely cause is one resolver host in Ivy's chain that times out on metadata lookups. sbt's chain resolver queries every resolver for each module, so one dead host costs ~20 s per module per lookup. It doesn't serve any artifact we use. Both the launcher's repositories and sbt's default build resolvers include repo.scala-sbt.org (scalasbt/maven-releases, ivy-snapshots, sbt-plugin-releases), and every artifact we actually fetched came from the other two hosts. That makes repo.scala-sbt.org the prime suspect, but this is still unconfirmed. I couldn't check it from this session, because the agent proxy blocks the host.

    What that means for the fix:

    • Adding -Dsun.net.client.defaultReadTimeout likely won't help. Requests already time out; there are just ~100 of them in a row.
    • A timeout bound plus one retry on each warm sbt ... update turns a 45-min silent stall into a fast, clear failure. But the stall lasted at least 05:53 to 09:47 today, so a retry minutes later would likely stall again.
    • The fix that addresses the cause is to keep the stalling host out of the warm boots' resolver chain. For example, a repositories file plus -Dsbt.override.build.repos=true limited to local, Maven Central and typesafe-ivy-releases (still needed for 0.13.x). This has to match what the docker_e2e_sbt cells resolve with, or Ivy's resolver-tagged cache entries won't be reused and the cells boot cold. It needs a real docker build on a runner to confirm, which this session can't do.
    • Before choosing, a cheap diagnostic: a dispatch run with -debug on the 1.2.8 warm boot (or -Divy.message.logger.level=4) will name the host that times out.

    No PR from this run: I can't validate a resolver change without a runner-side image build. This isn't merge-blocking (coverage-docker and e2e-docker only run on schedule and dispatch), so it stays a step-B candidate for a run that can confirm the host.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 23, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Re-verified (CI lane, 2026-10-23 07:5x UTC). The stall did not recur on the next nightly: run 39907645034 (aa69125) built the sbt image in normal time, with coverage-docker (sbt) at 05:52:56 to 05:57:01 (4 min) and e2e-docker (sbt) at 05:52:56 to 05:58:38 (6 min). Both e2e-extended sbt 1.13.0 legs also passed in 2 to 4 min.

    That fits the transient slow-resolver diagnosis in the comment above: all 4 hangs fall inside 2026-10-22 05:53 to 09:47 UTC. Not claiming a fix this run. A timeout bound on the warm sbt update calls would only shorten a future stall, not make the leg pass. If it shows up again on a nightly, that is the time to pin the build resolvers (~/.sbt/repositories with -Dsbt.override.build.repos=true) so the warm step skips repo.scala-sbt.org.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions