ReasonedError

ReasonedError is a StructuredError subclass that adds a stable reason code.

It is used when an error needs not only a human-readable message and structured details, but also a machine-readable classifier.

Why it exists

Some errors must be interpreted programmatically.

A class name alone is often too coarse:

InvalidFunctionArgumentError

while the actual reason may vary:

empty
wrong_type
too_long

Relying on exception messages for this is fragile. ReasonedError introduces a dedicated field (reason_code) for this purpose.

Basic usage

from mvx.common.errors import ReasonedError

error = ReasonedError(
    message="Invalid function argument",
    reason="empty",
    details={"argument": "username"},
)

payload = error.to_log_payload()

The log payload includes the reason when present:

payload = {
    "reason": "empty",
    "kind": "ReasonedError",
    "message": "Invalid function argument",
    "details": {
        "argument": "username",
    },
}

Reason codes

Reason codes should be:

  • short;

  • stable;

  • machine-readable;

  • safe for logging.

Good examples:

empty
wrong_type
too_long
not_found
already_closed

Bad examples:

User name is empty
Something went wrong
Invalid value passed to the function

Using StrEnum

A recommended practice is to define reason codes using StrEnum.

This provides:

  • a closed set of allowed values;

  • IDE support and discoverability;

  • protection against typos;

  • compatibility with logging and serialization.

Example:

from enum import StrEnum

from mvx.common.errors import ReasonedError


class ValidationReason(StrEnum):
    EMPTY = "empty"
    WRONG_TYPE = "wrong_type"
    TOO_LONG = "too_long"


error = ReasonedError(
    message="Invalid function argument",
    reason=ValidationReason.EMPTY,
    details={"argument": "username"},
)

StrEnum values behave like strings:

reason = str(ValidationReason.EMPTY)

This makes them safe to use in logs, JSON, and other external representations.

Design rule

Use ReasonedError when the reason must be treated as data.

If the error only needs a message and structured context, use StructuredError or a direct subclass of it.

API

class mvx.common.errors.ReasonedError(*, message, details=None, cause=None, reason=None)

Bases: StructuredError

Structured error with an optional stable reason code.

Extends StructuredError by adding reason_code, which can be used as a machine-readable classifier for logging, metrics, and tests.

Parameters:
  • message (str) – Human-readable error message.

  • details (Optional[Mapping[str, Any]]) – Optional log-friendly diagnostic context.

  • cause (Optional[Exception]) – Optional underlying exception.

  • reason (Optional[str]) – Optional stable reason code.

to_log_payload()

Extend base log payload with the reason code.

Return type:

dict[str, Any]

Returns:

Log payload including “reason” when present.