Skip to content

[BUG] - [BUG] - Windows: stdout uses the system code page, so JSON traces are not UTF-8 and unencodable characters crash the node #6711

Description

@Crypto2099

Internal/External
External

Area
Other: tracing / stdout output on Windows.

Summary
On Windows, cardano-node writes its stdout trace output in the process's legacy code page instead of UTF-8. Two consequences follow from one cause:

  1. A non-ASCII character that exists in the code page is written in that code page's byte form, so the JSON lines on stdout are not valid UTF-8. With TraceOptionNodeName: "talosü", the Reflection.TracerConfigInfo line contains "ApplicationName":"talos\x81". 0x81 is ü in OEM code page 437, the test host's OEM code page; UTF-8 would be C3 BC and CP1252, the host's ANSI code page, would be FC.
  2. A character that does not exist in the code page makes the stdout writer thread throw, and because that thread is linked to the main thread, the node exits at startup, before it opens its database: <stdout>: commitAndReleaseBuffer: invalid argument (cannot encode character '\12373').

The host name is the wider path: every trace line carries it, in the "host" field of the JSON format and in the prefix of the human-readable format, and it comes from getHostName independently of TraceOptionNodeName. TraceOptionNodeName itself also defaults to the host name. On a machine whose computer name contains non-ASCII characters, every trace line is therefore affected under the stock configuration. Characters outside the code page (for example a Japanese, Chinese or Korean name on a machine whose code page is 437) stop the node at its first trace line; this was reproduced on 11.1.2 by setting the host name through TRACE_DISPATCHER_LOGGING_HOSTNAME.

The same applies to stderr: when an error message contains a path the code page cannot represent, such as one under a user profile whose name uses characters outside the code page, writing it fails, the encoding error replaces the original exception, and the message is cut off.

Steps to reproduce

  1. On Windows with an English locale (test host: ANSI code page 1252, OEM code page 437), take a stock preprod config.yaml and add one key: "TraceOptionNodeName": "talosü". Save the file as UTF-8.
  2. Start the node with stdout redirected to a file: cardano-node.exe run --config config.yaml --topology topology.yaml --database-path db --socket-path \\.\pipe\node.socket --port 3001 > out.txt 2> err.txt
  3. Stop it after a few seconds and inspect out.txt as bytes. The Reflection.TracerConfigInfo line contains 74 61 6c 6f 73 81 22 (talos, 0x81, "), which is not valid UTF-8.
  4. Repeat with "TraceOptionNodeName": "さん" (U+3055 U+3093). The node exits almost immediately and err.txt contains the exception quoted below.
  5. Control: "TraceOptionNodeName": "talos-x" runs normally and every stdout line is valid UTF-8.
  6. Host name path: keep an ASCII TraceOptionNodeName and set the environment variable TRACE_DISPATCHER_LOGGING_HOSTNAME=Günther (or さん) before starting the node. This sets the same hostname value that the node otherwise takes from the computer name.

The configuration file is read correctly in every case: the name reaches the tracer as the intended characters, and only the stdout encoding differs.

Observed output

The 11.0.1 runs used the same binary, a fresh empty database on preprod, and the preprod config.yaml (UseTraceDispatcher: true) with only TraceOptionNodeName added.

TraceOptionNodeName Result stdout lines not valid UTF-8
talos-x runs normally 0
talosü runs 1 ("ApplicationName":"talos\x81")
Günther runs 1 ("ApplicationName":"G\x81nther")
さん exits at startup 0 (output stops mid-line)
Günther + U+30FC + さん exits at startup 1 ("ApplicationName":"G\x81nther, then output stops)

stdout for talosü, start of line 3:

{"at":"2026-09-29T21:03:56.5008163Z","ns":"Reflection.TracerConfigInfo","data":{"conf":{"ApplicationName":"talos\x81","Forwarder":{"maxReconnectDelay":30,...

The bytes around the name are 74 61 6c 6f 73 81 22 2c.

For さん, stdout ends mid-line at {"at":"2026-09-29T21:31:37.4368255Z","ns":"Reflection.TracerConfigInfo","data":{"conf":{"ApplicationName":" and stderr contains:

cardano-node.exe: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.SomeAsyncException:

ExceptionInLinkedThread (ThreadId 5) <stdout>: commitAndReleaseBuffer: invalid argument (cannot encode character '\12373')

While handling ExceptionInLinkedThread (ThreadId 5) <stdout>: commitAndReleaseBuffer: invalid argument (cannot encode character '\12373')

HasCallStack backtrace: throwIO, called at src/Cardano/Node/Handlers/TopLevel.hs:90:14 in cardano-node-11.0.1-9A2S7hSNaFUBV0TaMLmesA:Cardano.Node.Handlers.TopLevel

The combined name produces the same exception for '\12540' (U+30FC), after ü has already been written as 0x81.

Reproduced on 11.1.2 (the Windows binary distributed with Daedalus Mainnet 11.4.0, package id cardano-node-11.1.2-BZrVyWOIS7QHm71yCoNt2b), mainnet configuration, same method:

TraceOptionNodeName TRACE_DISPATCHER_LOGGING_HOSTNAME Result stdout lines not valid UTF-8
daedalus daedalus runs normally 0 of 375
さん unset (host talos) exits at startup, cannot encode character '\12373' 0 (output stops at "ApplicationName":")
talos-x さん exits at startup, cannot encode character '\12373' 0 (output stops at "host":" in the first JSON trace line)
talos-x Günther runs 374 of 376: every trace line carries "host":"G\x81nther"

The 11.1.2 crash is rethrown from
src/Cardano/Node/Handlers/TopLevel.hs:161:18.

stderr, for an error whose message contains a path under a folder named
Güntherーさん (11.1.2, excerpt):

... CheckpointsReadFileError "C:\\Users\\...\\G\252nther\12540\12373\12435\\...\\checkpoints.json" C:\Users\...\G\x81nthercardano-node.exe: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.IOException:

<stderr>: commitBuffer: invalid argument (cannot encode character '\12540')

Expected behavior
stdout, and in particular the machine-readable JSON trace lines, is UTF-8 on every platform regardless of the system code page, a character in a configured or derived name never terminates the node, and error messages on stderr are written in full.

Cause

Neither cardano-node nor trace-dispatcher sets an encoding on stdout, so the handle keeps GHC's default.

11.1.3 was not run; its main has no encoding setup either.

Suggested fix

Set UTF-8 on the standard handles and the locale encoding at the top of main, before anything writes to stdout:

import qualified GHC.IO.Encoding as GHC
import           System.IO (hSetEncoding, stderr, stdout, utf8)

main :: IO ()
main = do
  GHC.setLocaleEncoding utf8
  hSetEncoding stdout utf8
  hSetEncoding stderr utf8
  Crypto.cryptoInit
  ...

The explicit hSetEncoding calls make the result independent of whether stdout has already been evaluated when the locale encoding changes. Precedent: cardano-cli sets a UTF-8 locale encoding at startup since #4018 (
cardano-cli/app/cardano-cli.hs:46 at cardano-cli-11.2.3.1
), after #3487 and #4244 reported the same commitAndReleaseBuffer error on Linux. cardano-wallet wraps main in withUtf8 from the with-utf8 package (lib/application/app/shelley/cardano-wallet.hs:199 at v2026-09-16).

Separately, an exception in the stdout writer thread terminates the whole node through link, so a logging failure stops block production and sync.

Workaround (not a fix)

Until stdout is UTF-8, an operator on an affected machine can keep non-ASCII text out of the trace output by setting both the TRACE_DISPATCHER_LOGGING_HOSTNAME environment variable and TraceOptionNodeName to ASCII values. This avoids the crash and the invalid bytes for these two fields only; any other non-ASCII string that reaches a trace line still hits the same encoding path. On Linux, running under a UTF-8 locale (for example LANG=C.UTF-8) avoids it.

System info (please complete the following information):

  • OS Name: Windows (x86_64), English locale
  • OS Version 25H2 26200.9457
  • Code pages: ANSI 1252, OEM 437 ([System.Text.Encoding]::Default.CodePage and
    CultureInfo.CurrentCulture.TextInfo.OEMCodePage)
  • Node versions tested: cardano-node 11.0.1 (Windows binary distributed with
    Daedalus Pre-Prod 11.3.0, package id
    cardano-node-11.0.1-9A2S7hSNaFUBV0TaMLmesA) and cardano-node 11.1.2 (Windows
    binary distributed with Daedalus Mainnet 11.4.0, package id
    cardano-node-11.1.2-BZrVyWOIS7QHm71yCoNt2b). The source analysis above is
    against 11.1.2 and 11.1.3.

Screenshots and attachments
Raw stdout and stderr captures and the exact config.yaml for each run are available on request.

Linux

Tested on Linux with cardano-node 11.0.1 linux-x86_64 (git 97036a66, the static musl release binary), with the same configuration keys. The host name was also set to each test value with unshare -Ur --uts and sethostname.

Locale talos-x Günther さん Günther + U+30FC + さん
LANG=C.UTF-8 runs, valid UTF-8 runs, valid UTF-8 runs, valid UTF-8 runs, valid UTF-8
LC_ALL=C runs, valid UTF-8 exits 1 exits 1 exits 1

Under LC_ALL=C, any non-ASCII host name or node name stops the node at the first non-ASCII character it traces, with <stdout>: commitAndReleaseBuffer: invalid argument (cannot encode character '\252') ('\12373' for さん) and exit status 1. The human-format prefix on each line followed the host name set in the namespace, confirming that the host name reaches every trace line.

A Latin-1 locale could not be tested: the static musl binary ignores the locale and emits UTF-8.

Additional context
The node was launched by a PowerShell script through .NET System.Diagnostics.Process with UseShellExecute = false, CreateNoWindow = true and stdout and stderr redirected to pipes, then the pipes were copied byte for byte to files. The talos-x control and the Günther, さん and combined runs used the same non-ASCII install and database directories, and the talosü run used an ASCII directory, so the file paths are not a factor: the first stdout line, which prints the configuration with Haskell show, escapes those paths to ASCII.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs triageIssue / PR needs to be triaged.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions