It said it finished. Why is the output empty?
The job said it finished. The output is empty, short, or subtly incomplete. Five shapes of silent partial failure, how to recognise each, and the one habit that catches all of them.

It said it finished. Why is the output empty?
An automated job reported success and delivered nothing useful. This is silent partial failure, and it is the most common way AI work goes wrong at scale. Nothing errors. No alert fires. The run completes, the log says done, and the result is empty, truncated, or quietly missing the part you needed. Five shapes it takes, and the one habit that catches all of them.
The reason it is worth naming is that everything about the experience says the work succeeded. There is no red text to search for. The only signal is a number that is smaller than it should be, and nobody is looking at the number.
Shape one: it stopped at a round number#
You asked for a list and got exactly 100 results. Or exactly 500. Or exactly 1,000.
A result that lands exactly on a round number is almost never a coincidence. It is a limit, and the thing you actually needed is usually in the part that fell off, because the rare item is what you were looking for.
How to recognise it: the count matches the limit exactly. How to fix it: raise the limit until the count comes back under it, then treat that as the complete set. Not before.
Shape two: the pieces came back in the wrong order#
Work sent off in a batch, results matched back to the inputs by something that looked like a stable identifier and was not.
This happens whenever results are paired to inputs by a value the model retyped: a URL, a title, a name. Models rewrite those. A long web address comes back subtly different, a title gets its capitalisation tidied, and the row is now attached to the wrong input.
How to recognise it: results that are individually sensible and collectively wrong. The summary of item four is a perfectly good summary of item seven.
How to avoid it: match by position in the batch, not by anything the model echoed back.
Shape three: something else killed it#
A long job stopped partway and the thing that stopped it was not the job.
The most common version is a process being read while it runs. Piping a long-running job into something that truncates output can end the job itself, halfway, with no error. Another version is work started inside a session that ends: when the session closes, the work dies with it, having reported that it started.
How to recognise it: the output stops mid-item, at no meaningful point, with no completion message.
How to avoid it: write results to a file as they complete rather than at the end, and read the file. Long work should survive whatever you are doing while it runs.
Shape four: it saved the failure#
A step went wrong, the failure was written down as if it were the result, and every retry replayed the saved failure.
This is why a job sometimes fails instantly and identically forever. Nothing is retrying anything. It is reading a bad result that got recorded as good, and stopping.
How to recognise it: a job that used to take twenty minutes now fails in two seconds, every time, with the same message.
How to avoid it: only record a result after it has passed its own check. A response that failed validation gets discarded, not saved as a completed step.
Shape five: it wrote nothing, but wrote it successfully#
Several parallel workers, one output file. Each one opens it, writes, and closes it, and each one overwrites what the last one did. The run reports success from every worker and the file holds one worker's output, or none.
How to recognise it: the output is a fraction of what you expected, and a suspiciously tidy fraction.
How to avoid it: one writer per file. If several things produce output, each writes its own and something joins them afterwards.
The habit that catches all five#
Count what came out, and compare it to what should have.
That is the whole discipline. Before accepting any automated result, state the number you expect and check it against the number you got. Ten inputs, ten outputs. Four hundred rows in, four hundred rows out.
It sounds too simple to be the answer. It is the answer, because every failure above is invisible to every other test and obvious to this one.
| The shape | What to check |
|---|---|
| Stopped at a round number | Does the count equal the limit exactly? |
| Wrong pairing | Does result three actually belong to input three? |
| Killed by something else | Does the output end mid-item? |
| Saved failure | Does it now fail faster than it used to? |
| Overwritten output | Is the count a clean fraction of what you expected? |
I ran an automated job and I am not confident the result is complete. Help me verify it before I use it. Ask me, one at a time: 1. How many items went in, and how many came out? 2. Is the output count a round number, or exactly equal to any limit I set? 3. Pick three items at random: does each result actually correspond to the input it is attached to? 4. Does the output end cleanly, or does it stop mid-item? 5. Did this run finish faster or slower than usual? Then tell me whether this result is trustworthy, and if not, which of the failures it most likely is. Do not reassure me if the numbers do not match.
The uncomfortable part#
Every failure here produces a result that looks fine. That is what makes them expensive: they get used. The wrong number goes in the deck, the incomplete list goes to the client, and the discovery happens later, somewhere public.
Counting is not a sophisticated defence. It is just the one nobody runs, because the run said it finished.
Ahead pairs you with an expert operator who works on your own setup, in your own tools. If something you rely on runs unattended and you have never counted its output, that is a good first hour.

The check step: what goes between AI output and the person who gets it
A check step is the rule that runs between AI output and the person who receives it. Three kinds worth building, where to put them, and why the check should exist before the workflow does.

Context that holds: what to give AI about your work, and what to leave out
Most people paste context into a chat and watch it stop working three turns later. Here is how to build a context store that survives a long conversation, and what must never go in it.

Cursor or Claude Code, if you do not write code
Both are built for engineers, and the honest answer for a lot of non-coders is neither, yet. Here is what each one actually is, how to tell which suits you, and how to know when the answer is neither.