Custom Error Handler Library in C++
Make bytes_to_size() reentrant
Since bytes_to_size returns a pointer to a static buffer, it is not reentrant. This will be a problem if you have multiple threads throwing exceptions at the same time. There are two solutions:

Have bytes_to_sign() take a pointer to a buffer as an argument, and have it write the text into that.
Have it return a std::string.

Avoid mixing C and C++ string formatting
It's very weird to see both snprintf() and std::stringstream used at the same time. Now you will get the worst of both worlds: type unsafety and possible truncation from snprintf(), and the overhead of dynamically growing memory from std::stringstream. Pick one and stick with it.
Consider using std::format
C++20 introduced std::format, which gives you type-safe string formatting. If you want to be compatible with older versions of C++, you can use the {fmt} library.
About units
The SI prefixes are powers of 1000, if you want to make sure people are not confused, use the IEC binary prefixes, so KiB, MiB, GiB and so on. I would also add all the defined prefixes, so up to YiB.
Consider allowing different exception types to be thrown
Your THROW() adds helpful information to the exception message, but the exception type is fixed to std::exception. That means that it is hard for any code that wants to catch exceptions to determine what exactly went wrong has very little information. Maybe you can add the exception type as an argument to the macro?
#define THROW(type, text, ...) \
    error_handler::throw_ex&lt;type&gt;(text, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__);

template &lt;typename Exception, typename... Args&gt;
void throw_ex(const char* text, ...) {
    ...
    throw Exception(stream.str().c_str());
}

Then it can be used like so:
if (value &gt; MAX_VALUE) {
    THROW(std::out_of_range, &quot;value %d larger than maximum %d&quot;, value, MAX_VALUE);
}

C++20 will avoid the need for macros
C++20 introduces std::source_location which avoids the need to use macros to get the filename and line number at the place where the exception is thrown.