Output Streams

stdout is data, stderr is diagnostics

Output streams

stdout is data. stderr is diagnostics.

Every OctoMY™ program follows that one rule. What a program produces — a schema, a version string, a summary, a mesh report — goes to standard output. Everything it has to say about producing it — progress, warnings, errors, the whole log — goes to standard error.

That is what makes the output scriptable. You can pipe a program's stdout straight into a parser without filtering it, and you still see the diagnostics on your terminal.

Pro Tip

If a command's output looks right on screen but breaks when you pipe it, you are almost certainly capturing both streams. Redirect them separately — > data.json 2> run.log — and the problem usually disappears.


The worked example

construct schema prints a scene's parameter schema as JSON. Pipe it into any JSON consumer with nothing in between:

construct schema scene.construct | jq -r '.parameters[].name'

Piping schema output straight into jq

The diagnostics are still there — they went to stderr, so they appeared on your terminal instead of into jq. That is the whole rule in one frame: the parser got clean data, and you still got told what happened.

To keep them, send each stream to its own file:

construct schema scene.construct > schema.json 2> run.log

The two-stream redirect and the two files it leaves

Two files, both useful. schema.json parses; run.log tells you what happened while it was produced. Note the sizes — the log is 22 times the size of the data. That ratio is exactly why the two cannot share a stream: a data format that survives having twenty-two parts noise mixed into one part signal does not exist.

Both frames above are generated by running the commands, not by transcribing them — content/doc/scripts/screenshots/generate-stream-captures.sh, which also has a --check mode that fails if the tool's real output has drifted away from the picture.

To discard the diagnostics entirely:

construct schema scene.construct 2>/dev/null > schema.json

Which stream carries what

Every machine-readable mode writes its result to stdout:

Command On stdout Format
construct schema <file> The scene's parameter schema A single JSON object
construct validate <file> ok: N vars, N instances, N roots One line, and it is the first line
construct --version construct <version> One line
construct --help Usage text Plain text

Everything else — every log line at every severity, all progress reporting, and every error message — is on stderr.

Exit status is independent of both, and is the thing to branch on in a script. See the Construct CLI reference for the exit codes.


A note for existing scripts

This is a behaviour change. Before August 2026, log lines at debug and info severity were written to stdout, mixed in with the data. Warnings and above already went to stderr.

If you have a script that captures our output, this is what changes:

If your script… Then…
pipes stdout into a parser and it failed on log noise It now works. This was the bug.
captures stdout expecting to see log lines Those lines are on stderr now. Add 2>&1 if you genuinely want them combined.
captures both streams together already Nothing changes.
branches on exit status only Nothing changes.

If you ever worked around this by filtering our log format out of a data stream — grepping away lines starting D: [ or I: [, for instance — that workaround is no longer needed, and it is worth removing: it was throwing away any data line that happened to start the same way.


Why it is worth relying on

The rule is enforced, not merely documented:

  • A test emits a message at every severity and asserts that stdout receives nothing at all. Checking only debug would let a regression through that moved debug and left info behind.
  • The two machine-readable modes above are asserted by parsing their output, not by searching it. A test that searched for ok: would keep passing while the stream was polluted; one that requires it on the first line does not.
  • A static guard runs on every change and rejects any new write to stdout that is not on a reviewed list, so the rule cannot quietly erode.

Programs that behave this way compose. Programs that do not, do not.

In this section
Topics
reference CLI scripting stdout stderr automation
See also