What is the difference between stdout and stderr?
Standard output (stdout) is the stream a program writes its results to, and standard error (stderr) is a second stream for error messages and diagnostics. When a program runs in a terminal, both streams print there, so their text appears together. A pipe or a > redirection moves only stdout, and stderr still reaches the terminal.
Under normal conditions, a process starts with three standard streams, which are standard input (stdin), stdout, and stderr. A program reads its input from stdin. The Linux manual page numbers the three streams as file descriptors 0, 1, and 2.
The split lets a script or the next command in a pipeline read only the results, while a person still reads the errors. Checking which stream each message lands on is part of testing a command-line application.
What is stdout?
Standard output (stdout) is file descriptor 1, the stream for the output a program was run to produce. The Command Line Interface Guidelines recommend sending a command's primary output to stdout, along with any output that another program will read. Their reason is that stdout "is where piping sends things by default."
By default, stdout goes to the terminal. The > operator sends it to a file, and the | operator connects it to the standard input of the next command.
Where stdout points also changes how it behaves:
- Buffering. In C, stdout is usually buffered by line when it points to a terminal and in blocks when it points to a file or a pipe.
- Terminal checks. A program can check whether stdout is a TTY, which is a terminal device, e.g. with the shell test
[ -t 1 ]. Many tools turn off color and animation when it is not. - Closed pipes. When the reader of a pipe exits early, e.g.
head -n 5, the writer gets the SIGPIPE signal on its next write to stdout. By default, that signal ends the writer.
What is stderr?
Standard error (stderr) is file descriptor 2, the stream for messages about a run instead of its results. It usually carries three kinds of text:
- Errors. An error message says why the command failed, e.g.
error: cannot open orders.db. - Warnings. A warning reports a problem that the program worked around.
- Progress and logs. Status lines and spinners tell a person what the program is doing.
A C program writes to stderr with fprintf(stderr, ...), and a shell script adds >&2 to a command, e.g. echo "warning" >&2. In C, stderr is usually unbuffered, so each message is written at once, even when the program crashes right after it. The 2> operator sends stderr to a file, e.g. 2> errors.log.
Text on stderr does not mean that the command failed. The exit code reports success or failure, and a program can write warnings to stderr and still exit with 0. Scripts should check the exit code, not whether stderr is empty. When a command fails, its stderr text belongs in the reproduction steps next to the exact command.
When should a program write to stdout or stderr?
Here is an illustrative example. Acme Co. sells furniture online, and its command-line tool acme exports orders. A developer asks a coding agent to "Skip orders that have no delivery address and warn about each one." The change and its fix take five steps:
- The agent adds the warning with Python's
print(), which writes to stdout by default. - A nightly job runs
acme export --format json | jq length. - The line
warning: skipped order A-1042reachesjqahead of the JSON. - The job fails with
jq: parse error: Invalid numeric literal at line 1, column 8. - The developer changes the call to
print(message, file=sys.stderr).
A test with Python's subprocess module reads each stream on its own and catches the bug:
import json
import subprocess
def test_export_writes_only_json_to_stdout():
result = subprocess.run(
["acme", "export", "--format", "json"],
capture_output=True, text=True,
)
assert result.returncode == 0
json.loads(result.stdout) # fails if stdout holds anything but JSON
assert "skipped order A-1042" in result.stderr
The general rule has three parts:
- Results go to stdout. This includes output for other programs, e.g. the JSON that
--format jsonprints. A result on stderr skips the pipe. - Messages for a person go to stderr. Errors and warnings then stay visible when stdout goes to a file or a pipe.
- Requested help goes to stdout. Output from
--helpis a result, while a usage message after a bad flag goes to stderr.
This example is simplified. A real export would also need a test for the exit code when the order database cannot be opened.
What changes when a coding agent writes the code?
A coding agent can check its own change by running the command with 2>&1 added. Some agent shell tools also return both streams as one block of text. Either way, a warning on the wrong stream arrives in the same text as the results, so nothing marks it as misplaced. A test that the agent writes can merge the streams the same way and still pass.
Agent tools often capture output through a pipe, not a terminal. Spinners or color codes drawn without a terminal check then reach the agent as control characters.
One practical adjustment is a test that checks stdout and stderr separately. A golden file per stream also works, because a line that moves to the other stream then fails the comparison. A snapshot test that saves both streams in one file does not record which stream a line came from, so it can miss that move.
Can stdout and stderr be used together?
A shell can send each stream to its own place or merge the two. After the fix, acme export > orders.csv sends the order rows to the file, and the warnings still reach the terminal.
These Bash commands cover the common cases:
acme export > orders.csv # stdout to a file, stderr to the terminal
acme export 2> errors.log # stderr to a file, stdout to the terminal
acme export > orders.csv 2> errors.log # each stream to its own file
acme export > all.log 2>&1 # both streams to one file
acme export &> all.log # Bash short form of the line above
acme export 2>&1 | grep A-1042 # both streams into the pipe
acme export 2> /dev/null # discard stderr
The 2>&1 redirection makes file descriptor 2 a copy of file descriptor 1. Standard error then goes wherever stdout points at that moment, so the result depends on where 2>&1 sits. In acme export 2>&1 > all.log, only stdout reaches the file, because stderr was copied before stdout moved. A pipe carries only stdout, but Bash connects the pipe before the command's own redirections, so 2>&1 | sends both streams into it.
Merging has two costs. Once the streams share a file or a pipe, nothing marks which stream a line came from. Lines can also arrive out of order, because stdout waits in a block buffer while stderr is written at once.
A warning printed after 10 CSV rows can land in the file before them. Flushing stdout after each write keeps the order when both streams share one file. Python's -u option has the same effect, because it turns off buffering. A continuous integration and delivery (CI/CD) job log usually holds both streams too, so the same mix can appear there.
Spinners can "eat" output for a related reason. A spinner redraws one terminal line with a carriage return and an escape code that erases the line. If a log line from the other stream lands on that line between frames, the next redraw erases it or leaves it mixed with a spinner frame. The usual fix is to clear the spinner before printing a log line, and to draw spinners only when their stream is a TTY.
How do stdout and stderr compare?
The two streams compare on these points:
| Attribute | stdout | stderr |
|---|---|---|
| File descriptor | 1 | 2 |
| Carries | The results a program was run to produce | Errors and other messages for a person |
| Buffering in C | By line on a terminal, in blocks otherwise | Usually none |
| Buffering in Python | By line on a terminal, in blocks otherwise | By line |
| Sent through a pipe | Yes | Only after 2>&1 |
| Redirect to a file | > file | 2> file |
| Python call | print(text) | print(text, file=sys.stderr) |
| Signals failure by itself | No | No |
FAQs
What does 2>&1 mean?
The redirection 2>&1 makes standard error a copy of standard output, so both streams go to the same file or pipe. The copy points wherever standard output points at that moment, so 2>&1 goes after the redirection that sends standard output to a file.
Should warnings go to stderr?
Warnings should go to stderr, because they are messages for a person and not part of the result. On stderr, a warning stays visible when stdout goes to a file or a pipe, and it cannot end up inside data that another program reads.
Why do stdout and stderr lines appear out of order?
Lines from stdout and stderr can appear out of order when both streams share one file or pipe. In C and Python, stdout then waits in a block buffer while stderr is written at once, so a later warning can land before earlier results. Flushing stdout after each write keeps the order in a shared file.
How do you capture stderr in a test?
A test captures stderr by running the command with each stream collected on its own, e.g. with Python's subprocess module. The test then makes one check each for the result on stdout, the message on stderr, and the exit code.