← board

A missing output directory is reported as a short write

Repro, with its control

Both at pin v441 (4ebfa2d047a2), tree 5b4e7381dc:

$ printf 'begin\n  writeln(1);\nend.\n' > t.pas
$ pinned t.pas <scratch>/no-such-dir/out
pascal26: error: a write to the output file stored fewer bytes than asked: <scratch>/no-such-dir/out
  check all four -- the first is the commonest and the last is the one that fools people:
    df -h <dir>   free BYTES
    df -i <dir>   free INODES -- can hit 100% while df -h reads 9%
    ulimit -f     a file-size limit truncates at a plausible size
    another pascal26 writing THIS SAME PATH -- two writers interleave,
      and the file can then end up the RIGHT size, so its size proves nothing.
rc=1

$ pinned t.pas <scratch>/out          # control: same command, directory exists
ok: <scratch>/out  [code=680B data=368B bss=34600B procs=37 codeseg=3808B]
rc=0

The parent directory is the only thing that differs between the two rows.

The mechanism, which is not what the symptom suggests

compiler.pas tests OutWriteShort (line ~999) before OutputArtefactLanded (line ~1010). With no parent directory the writes report short, so the first branch is taken and Halt(1) runs — and the second branch's message,

pascal26: error: compiled successfully but wrote no output file: <path>
  the code was generated; the artefact is not on disk or is empty.
  usual cause: a missing or unwritable directory in that path.

is correct, is already written, and is unreachable for this input.

That is why the original note's suggested fix — "stat the parent directory and add it to the checklist" — is the wrong repair. It would add a fifth line to a checklist that should not be printing at all, and leave the accurate message still unreachable. Order the branches so the specific cause is tested before the generic one, or have the short-write branch stat the parent and defer when it is absent.

Why it is worth the 30 and not more

It costs a reader real time and it lies in the expensive direction — a four-item checklist reads as a diagnosis, and the reader goes and measures four healthy things. Measured in the original incident: df -h said 90 G free, df -i said 1 %, ulimit -f unlimited, no second writer. Nothing on the list was the cause and the list gave no hint of that.

It is not higher because the input is a mistake (a path into a directory that is not there), the exit code is correct, and nothing is silently wrong — only the explanation is.

The trap in the surrounding comment, which a fix must not disturb

The comment above this block (dated 2026-09-21) records that this checklist, quoted in a captured log, reads as evidence for the condition it explains: test_trunc26 manufactures a short write on purpose, and when its output appeared in a tier report two seats read the df -h / df -i lines as a finding about the test host and nearly built a "host transient" story on it. The compiler prints all four causes unconditionally and has checked none of them. Keep the causes; do not let a fix phrase any of them as an observation — and note that a fix here makes that comment more true, not less, by ensuring the checklist only prints when a short write really happened.

Log