# Diagnose a hanging or long-running command in Scala with Atlas (2026)

> Atlas races every Scala command against a timeout and, when it expires, says whether to retry with a larger timeout or whether the sbt process is blocked waiting for interactive input.

A hanging Scala command is almost always one of two things: an sbt task that is genuinely slow, or a process silently blocked waiting on stdin. Atlas's bash tool races every command against a timeout, and when the timeout expires the shell_metadata block tells you exactly what happened and what to do, namely retry with a larger timeout if the command is expected to take longer and is not waiting for interactive input. That second clause is the whole diagnosis. A blocked-on-stdin sbt process never resolves by waiting, so the fix is to re-run with non-interactive flags rather than raising the timeout. Atlas uses bash and read for this workflow, nothing more.

## Key takeaways

- Atlas's bash tool races every sbt command against a timeout and reports the outcome in a shell_metadata block.
- A blocked-on-stdin sbt process never resolves by waiting, so raising the timeout is the wrong fix for it.
- Re-run with -y, --no-input, or CI mode to make prompting impossible and convert a hang into a real error.
- Retry with a larger timeout in milliseconds only when Atlas says the command is not waiting for interactive input.
- User aborted the command distinguishes your own interrupt from a genuine timeout, so the diagnosis stays honest.

## Why does my sbt command hang forever and how does Atlas tell me why?

Atlas's bash tool races every command against a timeout, so a hanging sbt task is killed rather than left to spin. The shell_metadata block then names 1 of 2 causes: the command is genuinely slow and should be retried with a larger timeout, or it is waiting for interactive input, which is the most common reason an sbt build stalls.

An sbt session that appears frozen is doing one of two things. Either sbt is genuinely working, resolving Ivy coordinates, compiling a large src/main/scala tree, running a slow ScalaTest suite, or sbt is sitting at a prompt nobody can see, waiting for a keystroke that will never come. Guessing between those two wastes real time, because the remedies are opposite: one wants more patience, the other wants none. Atlas removes the guess. When the bash tool's timeout expires, Atlas kills the process and emits a shell_metadata block explaining that you should retry with a larger timeout if the command is expected to take longer and is not waiting for interactive input. Reading that message is step one of the diagnosis, and Atlas's read tool is the only other tool this workflow needs.

## How do I tell whether sbt is slow or blocked on stdin?

Read the shell_metadata block Atlas emits when the bash timeout fires. Atlas explicitly calls out the interactive-input case, so a Scala developer in 2026 can distinguish an sbt compile that needs 300 seconds from an sbt task that is parked at a hidden prompt and will never finish.

The distinction is behavioral, not visual, because both look identical in a terminal: no output, no exit. In a Scala project, blocked-on-input usually means an sbt plugin asking for a credential, a Coursier fetch waiting on a repository password, a release plugin asking to confirm the next version, or a scalafmt check that stopped for confirmation. Genuinely slow usually means a cold compile of a large module, a full ScalaTest via sbt test run, or dependency resolution against a slow artifact host. Atlas's message names the interactive case explicitly, so the reader is not left inferring. Once the category is known, the fix follows mechanically: non-interactive flags for the blocked case, a larger timeout in milliseconds for the slow case.

## What non-interactive flags stop an sbt command from prompting?

Atlas names 3 non-interactive levers when it reports that a command may be waiting for interactive input: -y, --no-input, and CI mode. Re-run the sbt command through bash with those flags so the process cannot prompt, because a command that cannot prompt either completes or fails, and both outcomes beat a hang.

The rule is simple: make prompting impossible, then see what happens. Whichever tool in the Scala chain is asking the question, sbt itself, a plugin, Coursier, or a downstream script, the same three shapes cover almost all of them: -y, --no-input, or a CI-mode switch. Running under CI mode is the strongest form, because it tells the whole toolchain that no human is present. When the sbt task then fails with a real error about a missing credential or an unconfirmed version, you have converted an invisible hang into a visible, actionable failure. That is the entire point of this step. Atlas's bash tool is a real shell, so the flags you would type by hand are the flags Atlas runs.

## How do I raise the timeout for a slow sbt test run in Atlas?

Atlas tells you to retry with a larger timeout value in milliseconds when its bash tool reports the command is genuinely slow rather than blocked, exactly as the message instructs. A cold ScalaTest via sbt test run on a multi-module build in 2026 routinely outlives a default timeout, and raising it is the correct answer, not a workaround.

Scala builds are honestly slow in ways that surprise people coming from faster ecosystems. A clean compile of a large sbt module, with macros and implicit resolution, plus a full ScalaTest via sbt test pass, can take many minutes. Atlas's bash timeout is expressed in milliseconds, so a longer budget is a single numeric change on the retry. The important discipline is to only do this after the metadata has told you the command is not waiting for interactive input. Raising the timeout on a blocked process just buys a longer hang. Where the run is long enough to be awkward, keeping the sbt shell warm and running scalafmt separately reduces how often the slow path is hit at all.

## What does User aborted the command mean in Atlas?

User aborted the command is the shell_metadata message Atlas emits when you interrupted the process yourself, and Atlas keeps it distinct from the timeout message, so the 2 outcomes never blur. A Scala developer who kills a long sbt run gets an unambiguous record of that choice rather than a false timeout diagnosis.

Three outcomes can end a long-running Scala command in Atlas, and confusing them leads to bad conclusions. A timeout means Atlas's bash tool killed the process after the budget expired. User aborted the command means you stopped it. A normal exit means sbt finished, whatever the exit code. Because Atlas records which of these happened in the metadata, an aborted sbt test run is never mistaken for a hang, and a hang is never mistaken for an abort. That distinction protects the diagnosis: you should not go hunting for a hidden prompt in a build that you yourself cancelled twenty seconds in because you remembered you were on the wrong branch.

## How does Atlas keep a hanging command from doing damage?

Atlas gates every tool call against 3 rule types, allow, ask, and deny, before it runs, so an sbt command that Atlas wants to execute is checked against your policy first. Atlas also drafts a plan in a read-only plan agent and asks before switching to a build agent.

Diagnosing a hang means running commands, and running commands is exactly the category of action that should not happen unsupervised. Atlas gates every bash invocation, so an sbt publish or a release task can be placed on the deny list while sbt test and scalafmt sit on allow. Because the plan agent is read-only, Atlas can reason about why a build is stalling, reading build.sbt, project/plugins.sbt, and the module layout, without executing anything. When Atlas does move to a build agent and edits a file, Atlas computes a unified diff for every file edit and surfaces it for approval before writing, and Atlas snapshots file changes as git patches so any change can be rolled back.

## Steps

1. Run atlas in a project with a build.sbt so Atlas can see your sbt modules, project/plugins.sbt, and .scalafmt.conf.
2. Run the stalling command, such as sbt test or a full ScalaTest via sbt test pass, through Atlas's bash tool.
3. When the command is killed, read the shell_metadata block in the output with Atlas's read tool.
4. Decide from the message whether the command is slow or blocked; Atlas explicitly calls out the interactive-input case.
5. If it is blocked, re-run through bash with the tool's non-interactive flags (-y, --no-input, CI mode) so sbt cannot prompt.
6. If it is genuinely slow, retry with a larger timeout value in milliseconds, as the message instructs.
7. If you aborted the run yourself, note that the metadata says User aborted the command, which distinguishes your interrupt from a timeout.
8. Once the command completes, re-run scalafmt and sbt test cleanly to confirm the build is healthy end to end.

## FAQ

### why does sbt test hang and never finish

Either the ScalaTest run is genuinely slow, or the sbt process is blocked waiting on stdin, often for a credential or a version confirmation. Atlas's bash tool races the command against a timeout and the shell_metadata block tells you which case you are in, because Atlas explicitly calls out the interactive-input case.

### how do I increase the command timeout in atlas

Retry the command through Atlas's bash tool with a larger timeout value in milliseconds, exactly as the timeout message instructs. Do this only after the metadata has confirmed the command is not waiting for interactive input, because a blocked process will just hang for longer.

### how do I stop sbt from prompting for input

Re-run the command with the tool's non-interactive flags, meaning -y, --no-input, or CI mode, through Atlas's bash tool. A command that cannot prompt either completes or fails with a real error, and a visible failure is always better than an invisible hang.

### what is shell_metadata in atlas bash output

shell_metadata is the block Atlas's bash tool emits alongside command output. For a hanging Scala command it carries the diagnosis: whether to retry with a larger timeout, whether the command may be waiting for interactive input, or whether the metadata says User aborted the command.

### does atlas know the difference between a slow build and a stuck build

Yes. Atlas races every command against a timeout and, when it expires, tells you to retry with a larger timeout if the command is expected to take longer and is not waiting for interactive input. That conditional is the diagnosis, and it separates a slow sbt compile from a process blocked on stdin.

### can atlas run sbt commands without asking me

Only if you allow it. Every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs, so sbt test and scalafmt can sit on allow while a publish or release task sits on deny.

### what do I need to set up atlas on a scala project

Run atlas in a project with a build.sbt. Atlas reads your traits, implicits, and sbt modules from there, and can refactor to typeclasses or add ScalaTest cases, showing you the diff for review before writing.

### can atlas investigate a stuck build without changing anything

Yes. Atlas drafts a plan in a read-only plan agent and asks before switching to a build agent, so it can read build.sbt and project/plugins.sbt and reason about the stall without executing or editing anything.

---

Canonical HTML: https://runatlas.sh/resources/stacks/diagnose-a-hanging-or-long-running-command-in-scala
Source of truth: aeo_pages row `/resources/stacks/diagnose-a-hanging-or-long-running-command-in-scala` (segment: Stacks) (this file is generated from it, never hand-edited).
Licence: Atlas is proprietary with a free core. It is not open source and there is no public source repository.
