Repository navigation
An outer <PrintErrors> does not print a failure an <Output> region decided #327
Description
Motivation
architecture.md is authoritative on the error model, and its mode table says:
| Mode | An undecided error… | Installed by |
|---|---|---|
output |
fails the run; <PrintErrors> can print instead |
every <Output> region |
§6.8.1 says the same from the other side: a failure an output decision selected is deliberately not in the never-printed list — "the region it left has already stopped, so printing it resumes nothing." throw is named as the one mode a boundary does not replace.
On 511776e an outer <PrintErrors> does not print such a failure. The run fails instead. The contract is the architecture's; this is the engine not meeting it.
Reproduction
Shared fixture — a name that resolves to nothing, which raises an ordinary printed error under print:
<!-- components/OutFail.md -->
<Output>before <Nope /> after</Output><!-- components/Holder.md -->
<Output><Content /></Output>Control — print at the root works.
<PrintErrors>
<Nope />
</PrintErrors>
TAIL REACHEDexit 0; stdout carries <!-- ERROR: Failed to import component Nope … --> and TAIL REACHED. Correct.
Case 1 — the failure is in the callee's own <Output> region.
<PrintErrors>
<OutFail />
</PrintErrors>
TAIL REACHEDExpected: the boundary prints one error whose cause is the original failure, and TAIL REACHED renders.
Actual: exit 1. stdout holds only the partial text before ; the message goes to stderr; TAIL REACHED never renders.
Case 2 — the failure is projected into a callee's <Output> region.
<PrintErrors>
<Holder><Nope /></Holder>
</PrintErrors>
TAIL REACHEDExpected: §6.3 says "the error mode is captured at the expansion site and carried across", and the expansion site here is inside <PrintErrors> — so this should print for that reason alone, independently of the output question.
Actual: exit 1, nothing on stdout, TAIL REACHED never renders.
Where to look
decidedByOutput() (packages/core/src/errors.ts:264) exists precisely to distinguish an output decision from a throw decision at a printing boundary, and its comment states the intended difference. Its only consumer is expand.ts:2292, on the ContentExpansionFailure branch. Case 1 does not reach that branch at all, and case 2 reaches it but still fails — so the two cases may not share a cause. Both should be checked rather than assumed to be one fix.
Partial output
Case 1 shows the partial-output rule already working: before reached the output stream before the failure. Whatever fixes printing must keep that — the region's already-rendered text stays, and printing adds the error after it rather than replacing it.
Acceptance criteria
- An outer
<PrintErrors>turns a failure an<Output>region decided into exactly one printed error whosecauseis the complete original failure, and the run continues past the boundary. - The same holds whether the failure arose in the callee's own
<Output>region or in content projected into one. - A
throwdecision is still never printed — documentation and value roots keep failing through a boundary. - Text a failing region had already rendered still reaches the output stream, and the printed error does not replace it.
- A durability failure is still never printed.
- Tests discriminate the three cases above by exit status and by whether the tail after the boundary renders — an assertion on stderr alone would pass today.
Impact
Reported while synchronizing the #181 living end-goal target (#292). The workflow documents describe the architectural behavior, as architecture.md is authoritative, and classify this as missing pending this issue. No workflow document writes <PrintErrors> today, so nothing is blocked on it — but a stage that wants to survive a failed region cannot, and the target's error-mode table would otherwise have to contradict the registry.
Activity
📝 Align the target with #203 as merged and restore the printing contract
📝 Align the target with #203 as merged and restore the printing contract
📝 Align the target with #203 as merged and restore the printing contract
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
7 remaining items
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Say what this workflow does, now that it does it
📝 Align the target with #203 as merged and restore the printing contract
📝 Let the root be what settles this workflow's failures
📝 Say what this workflow does, now that it does it