# How fmtlib Uses `report_error()` for Compile-Time Format String Validation

> Discover how fmtlib leverages report_error() for compile-time format string validation, ensuring immediate compilation failure on malformed strings with clear error messages.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: internals
- Published: 2026-09-09

---

**`fmt::report_error()` is a `[[noreturn]]` function that throws `fmt::format_error` inside constexpr parsing contexts, forcing immediate compilation failure with a descriptive message when format strings are malformed.**

The {fmt} library provides type-safe formatting for C++ by validating format strings at compile time. Central to this mechanism is `report_error()`, defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), which acts as the bridge between the library’s constexpr parser and the compiler’s diagnostic engine. When the parser encounters invalid syntax, it invokes this function to abort compilation with a specific error message rather than deferring failure to runtime.

## `report_error()` Declaration and Implementation

The function serves as a lightweight, centralized error handler that transforms parsing failures into exceptions.

### Declaration in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)

At lines 652–658, `report_error` is declared with the `[[noreturn]]` attribute to indicate it never returns control to the caller:

```cpp
// https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h#L652-L658
FMT_NORETURN FMT_API void report_error(const char* message);

```

The `FMT_NORETURN` macro expands to `[[noreturn]]`, while `FMT_API` handles visibility for shared library builds. This signature accepts a static error message string that will propagate to the compiler diagnostic.

### Implementation in [`include/fmt/format-inl.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format-inl.h)

The implementation at lines 157–159 simply constructs and throws a `format_error` exception:

```cpp
// https://github.com/fmtlib/fmt/blob/main/include/fmt/format-inl.h#L157-L159
FMT_FUNC void report_error(const char* message) {
  throw format_error(message);
}

```

Because this function is marked `[[noreturn]]` and unconditionally throws, any constexpr evaluation reaching it immediately terminates, resulting in a compile-time error diagnostic containing the provided message.

## The Compile-Time Error Mechanism

fmtlib leverages C++20 constexpr semantics to perform validation during constant evaluation. The mechanism works because **constexpr functions are allowed to throw exceptions**, but if a throw is actually reached during constant evaluation, the compiler must treat it as a compilation failure.

When parsing a format string in a `constexpr` context (via `FMT_STRING` or `fmt::format_string`), the parser analyzes each character and brace pair. Upon detecting mismatched braces, invalid specifiers, or type mismatches, it calls `report_error("...")`. The compiler recognizes that this call throws during constant evaluation and aborts the compilation, displaying the error message passed to the function.

## How the Parser Uses `report_error()`

The constexpr parser resides primarily in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) and [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h), invoking `report_error()` whenever it encounters structural violations.

### Format String Parsing Flow

1. **Entry**: User code constructs `fmt::format_string<Ts...>` or uses the `FMT_STRING` macro, which triggers constexpr parsing.
2. **Validation**: The parser scans for `{`, `}`, and format specifiers. It verifies that opening braces have matching closers and that conversion specifiers are valid for the provided argument types.
3. **Error Injection**: When the parser detects `c == '{'` without a valid matching construct, it calls `report_error("invalid format specifier")`.
4. **Compilation Abort**: Because this call occurs in a constexpr context, the compiler emits a diagnostic like *"invalid format specifier"* and halts compilation.

### Example from [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)

At line 2164, the parser handles brace characters and invokes `report_error` for malformed input:

```cpp
// https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h#L2164
if (c == '{' && (next == '{' || next == '}')) {
  // Handle escaped braces
} else if (c == '{') {
  report_error("invalid format specifier");
}

```

If this branch executes during constant evaluation—for example, when processing `fmt::format(FMT_STRING("{d}"), 42)`—the compiler aborts with the message *"invalid format specifier"* before generating any runtime code.

## Practical Code Examples

### Valid Compile-Time Validation

When the format string is well-formed, the parser completes without reaching `report_error()`:

```cpp
// Compiles successfully: parser validates braces and types
auto msg = fmt::format(FMT_STRING("Value: {}"), 42);
static_assert(msg == "Value: 42");

```

### Invalid Format String Failure

Malformed strings trigger `report_error()` inside the constexpr parser:

```cpp
// Fails to compile: invalid format specifier detected
auto bad = fmt::format(FMT_STRING("Number: {d}"), 42);

```

The compiler output references the `report_error` call:

```

error: call to non-'constexpr' function 'void fmt::v10::report_error(const char*)'
note: in constexpr expansion of '...'
error: static assertion failed: invalid format specifier

```

### Printf-Style Validation

The same mechanism applies to `fmt::printf_string` in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h):

```cpp
// Valid: compiles without error
auto s1 = fmt::printf(FMT_STRING("%04d"), 7);

// Invalid: triggers report_error("invalid format specifier")
auto s2 = fmt::printf(FMT_STRING("%z"), 5);

```

## Summary

- **`report_error()`** is declared in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) (lines 652–658) as a `[[noreturn]]` API function that throws `fmt::format_error`.
- The implementation in [`include/fmt/format-inl.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format-inl.h) (lines 157–159) constructs the exception with the provided diagnostic message.
- **Compile-time validation** occurs because constexpr parsers in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) and [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h) invoke `report_error()` when detecting malformed format strings.
- **Error propagation** relies on C++20 constexpr semantics: throwing during constant evaluation forces a compilation error containing the `report_error()` message.
- The mechanism applies to both modern `fmt::format_string` and legacy `fmt::printf_string` wrappers.

## Frequently Asked Questions

### Why does throwing an exception inside a constexpr function create a compilation error?

In C++20, constexpr functions are permitted to contain throw expressions, but if the throw is actually evaluated during constant evaluation, the compiler must stop and report it as an error. `report_error()` exploits this by unconditionally throwing `format_error`, which turns runtime exception mechanics into static compile-time diagnostics.

### What types of format errors can `report_error()` catch?

The function handles structural parsing failures such as **mismatched braces**, **unknown format specifiers** (e.g., `{d}` instead of `{}`), **unescaped braces**, and **invalid printf-style conversions** (e.g., `%z`). It does not catch type mismatches between arguments and specifiers; those are handled separately via template constraints.

### Can I customize the error messages generated by `report_error()`?

No, the error messages are hardcoded into the parser at the specific failure sites in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) and related headers. While you cannot override the strings at runtime, you can wrap fmtlib calls in your own constexpr functions to provide additional context before the compiler displays the underlying `report_error()` diagnostic.

### Does this mechanism work with custom type formatters?

Yes. When you specialize `fmt::formatter<T>` for your type, the `parse()` method is executed in a constexpr context. If your custom parser calls `report_error()` upon encountering invalid specifiers, you inherit the same compile-time guarantees for your custom types.