Skip to content

A failed command is forgotten: the next build reports "Nothing to do" and exits 0 #187

Description

@typeless

Summary

A command that exits nonzero is not remembered. If its declared outputs exist on disk when the next build starts, that build reports "Nothing to do (up to date)" and exits 0 — so the failure disappears and the tree is left holding whatever the failed run produced, or whatever a previous successful run left behind.

Reproduced

mkdir p && cd p && touch Tupfile.ini
echo ORIGINAL > src.txt
printf ': src.txt |> cp %%f %%o |> out.txt\n' > Tupfile
putup configure -B . && putup -B .            # out.txt = ORIGINAL

echo "NEW CONTENT" > src.txt
printf ': src.txt |> cp %%f %%o && false |> out.txt\n' > Tupfile   # command now fails
build 2:  [.] Build completed: 0 commands (1 failed)     exit 1     out.txt = NEW CONTENT
build 3:  [.] Nothing to do (up to date).                exit 0     out.txt = NEW CONTENT

Build 3 reports success for a command whose only execution exited 1.

Why it matters

putup -k in CI, followed by any later putup, reports green. I hit this while iterating on a compile error: a build with failures was followed by "Nothing to do", and the link had silently reused stale .o files from the previous successful build. It took a deliberate probe to notice, because nothing in the second build's output suggests anything is wrong.

The control cases both behave correctly, which localizes it:

  • A failing command that produces no output keeps failing on every subsequent run (0 commands (1 failed), exit 1) — retry scheduling itself is fine.
  • Without -k, a single failing command also keeps failing, because the build aborts before recording anything.

So the trigger is: the run completed (with -k, or with the failure late in the schedule) and the failed command's outputs exist.

Suspected mechanism

Not verified — the observed behaviour is consistent with the index recording the command's input state after a run in which that command failed, so the next build's change detection sees nothing to do. Exit status does not appear to be part of the recorded state.

Suggested direction

A command's recorded state should not advance unless it succeeded. Either skip index update for failed commands, or persist a failed marker that forces a re-run regardless of output presence.

Provenance

Found incidentally while iterating on #185's compile errors. Reproduced from scratch with the recipe above before filing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TLob4Ef3qyMd3Dnp9q3XmA

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions