nodejs/node · critical

error at %s: %s

Error message

error at %s: %s

What it means

This is the generic fatal error handler for ICU build tools. IcuToolErrorCode is a RAII wrapper around UErrorCode; when an instance with a failure state is destroyed, handleFailure() prints 'error at <location>: <errorName>' and calls exit(errorCode). The location string is set when the IcuToolErrorCode is constructed or assigned, identifying which tool operation failed.

Source

Thrown at deps/icu-small/source/tools/toolutil/toolutil.cpp:85

#include "unicode/errorcode.h"
#include "unicode/putil.h"
#include "unicode/uchar.h"
#include "unicode/umutablecptrie.h"
#include "unicode/ucptrie.h"
#include "cmemory.h"
#include "cstring.h"
#include "toolutil.h"
#include "uassert.h"

U_NAMESPACE_BEGIN

IcuToolErrorCode::~IcuToolErrorCode() {
    // Safe because our handleFailure() does not throw exceptions.
    if(isFailure()) { handleFailure(); }
}

void IcuToolErrorCode::handleFailure() const {
    fprintf(stderr, "error at %s: %s\n", location, errorName());
    exit(errorCode);
}

namespace toolutil {

void setCPTrieBit(UMutableCPTrie *mutableCPTrie,
                  UChar32 start, UChar32 end, int32_t shift, bool on, UErrorCode &errorCode) {
    uint32_t mask = U_MASK(shift);
    uint32_t value = on ? mask : 0;
    setCPTrieBits(mutableCPTrie, start, end, mask, value, errorCode);
}

void setCPTrieBits(UMutableCPTrie *mutableCPTrie,
                   UChar32 start, UChar32 end, uint32_t mask, uint32_t value,
                   UErrorCode &errorCode) {
    if (U_FAILURE(errorCode)) { return; }
    // The value must not have any bits set outside of the mask.
    if ((value & ~mask) != 0) {

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Read the error name in the message to identify the underlying ICU error code.
  2. Use the location string to find which tool function or operation failed.
  3. Address the root cause indicated by the specific UErrorCode value (e.g., fix the parse error, provide the missing file).
  4. If developing a custom tool, check the IcuToolErrorCode before it goes out of scope and handle failures gracefully instead of relying on the destructor's exit().

Example fix

// before — unhandled error code falls through to destructor
IcuToolErrorCode status("myop");
someIcuApi(&status);
// status goes out of scope, triggers exit()

// after — check before scope exit
IcuToolErrorCode status("myop");
someIcuApi(&status);
if (status.isFailure()) {
    fprintf(stderr, "custom handling: %s\n", status.errorName());
    status.reset();  // prevent destructor exit
    return 1;
}
Defensive patterns

Strategy: try-catch

Try / catch

// IcuToolErrorCode is a RAII wrapper — handle errors before scope exit
#include "toolutil.h"

void safeIcuOperation() {
    icu::IcuToolErrorCode status("myOperation");
    someIcuApi(&status);
    if (status.isFailure()) {
        // Handle gracefully instead of letting destructor call exit()
        fprintf(stderr, "Operation failed: %s at %s\n",
                status.errorName(), status.location);
        status.reset();  // Clear failure to prevent destructor exit
        return;
    }
    // Success path — status goes out of scope with no failure, no exit
}

Prevention

When it happens

Trigger: Any ICU build tool (genrb, gencnval, gennorm2, etc.) using IcuToolErrorCode that encounters a U_FAILURE code and lets the error code object go out of scope without being checked and handled. The destructor detects isFailure() and calls exit() with the numeric ICU error code as the process exit code.

Common situations: Any unrecoverable ICU error during data building — missing input files, corrupted data, parse failures in upstream code, invalid arguments to ICU APIs. The error message will contain the location tag and the ICU error name (e.g., 'U_PARSE_ERROR', 'U_MEMORY_ALLOCATION_ERROR').

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/faef6149c026512f. Report an issue: GitHub.