How fmtlib Uses `report_error()` for Compile-Time Format String Validation
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, 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
At lines 652–658, report_error is declared with the [[noreturn]] attribute to indicate it never returns control to the caller:
// 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
The implementation at lines 157–159 simply constructs and throws a format_error exception:
// 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 and include/fmt/printf.h, invoking report_error() whenever it encounters structural violations.
Format String Parsing Flow
- Entry: User code constructs
fmt::format_string<Ts...>or uses theFMT_STRINGmacro, which triggers constexpr parsing. - 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. - Error Injection: When the parser detects
c == '{'without a valid matching construct, it callsreport_error("invalid format specifier"). - 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
At line 2164, the parser handles brace characters and invokes report_error for malformed input:
// 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():
// 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:
// 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:
// 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 ininclude/fmt/core.h(lines 652–658) as a[[noreturn]]API function that throwsfmt::format_error.- The implementation in
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.handinclude/fmt/printf.hinvokereport_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_stringand legacyfmt::printf_stringwrappers.
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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →