DEV Community

Cover image for When a task misbehaves
VX
VX

Posted on Edited on Originally published at vznjs.github.io

When a task misbehaves

A task hangs, gets killed, or prompts for input. vx tells you what
happened in one line.

$ vx run packages/b#hang                 # exec: { command: 'sleep 30', timeout: '1s' }
┌─ packages/b#hang > $ sleep 30
[vx] timed out after 1000ms — killed (SIGTERM)
└─ packages/b#hang ── (1.01s) failed (timed out, exit 143)

$ vx run packages/b#killed               # exec: { command: 'kill -9 $$' }
┌─ packages/b#killed > $ kill -9 $$
[vx] exit 137 is how the shell reports a death by SIGKILL (9): nothing catches it — on Linux the kernel's OOM killer (dmesg, or the memory limit of the container's cgroup) or an explicit kill; vx's own timeout reports itself as a timeout
└─ packages/b#killed ── (5ms) failed (exit 137, 128 + SIGKILL)
Enter fullscreen mode Exit fullscreen mode

A hang becomes a named timeout. Exit 137 gets its signal and the usual
causes.

Demo workspace · vx 69617eb · 2026-10-23

exec.timeout takes a duration ('1s', '5m'). --timeout sets one
for the whole run.

No vx run inside a task

A task that calls vx run on its own workspace hides work from the
graph and the cache key, and a loop back to itself never ends. vx
refuses it:

$ vx run packages/b#nested
┌─ packages/b#nested > $ vx run packages/b#echo
✗ vx run  task packages/b#nested runs `vx run` inside its own workspace: a nested run is invisible to the outer graph (its tasks escape the schedule, the concurrency budget and the cache key) and a loop back to this task forks without bound

  Declare what it needs with dependsOn instead
└─ packages/b#nested ── (112ms) failed (exit 1)
Enter fullscreen mode Exit fullscreen mode

Running a different workspace from a task is fine.

A config typo names the line

$ vx lock
✗ vx lock  lock not written

  packages/b/vx.config.ts:9:42
  tasks.hang.exec.timeout takes a duration with its unit, like '1000ms' or '1s' (got 1000)

    7 |     },
    8 |     ...lib({ entry: 'src/index.ts' }),
  > 9 |     hang: { exec: { command: 'sleep 30', timeout: 1000 } },
      |                                          ^
Enter fullscreen mode Exit fullscreen mode

File, line and column. No stack trace.

Arguments after --

Everything after -- goes to the command and into the cache key, so
different flags never reuse the wrong result:

$ vx run packages/b#echo -- --watch=false
┌─ packages/b#echo > $ echo args: --watch=false
args: --watch=false
Enter fullscreen mode Exit fullscreen mode

Prompts get the terminal

A task with exec.interactive: true, like a migration that asks before
it applies, gets your terminal. It runs alone and is never cached. In
CI or a pipe its input is empty, so a prompt can't hang.

Two runs take turns

A second vx run in the same workspace waits for the first. After a
second it tells you:
[vx] waiting for another vx run (pid N) on this workspace to finish….

Every option is in the schema and the
CLI reference. More at
vznjs.github.io/vx.


Originally published on the vx blog. vx is open-source (MIT) task orchestration with a build cache: vznjs.github.io/vx · GitHub.

Written with AI assistance.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.