errors
Canonical error contract for Drafter.
This module defines the single normalized error envelope used across the Python server lifecycle, the Python bridge runtime, and (via JSON) the TypeScript bootstrap/debug UI.
Every failure should ultimately be describable as an ErrorDetails:
id: stable, code-like identifier (example:request.route_not_found).category: one ofCATEGORIES.severity: one ofSEVERITIES.message: technically concise, accurate message.details: developer-focused free-text details.data: structured, JSON-safe details (nested dicts/lists), for renderers that can display them richly. Well-known keys:route_call(the generated route call string, e.g.guess(state, 5)),route_call_exact(whether that call string is the fully-bound call or a best-effort approximation),request(the structured request description).friendly_title: short page-title-style label for the failure.friendly_message: plain-language explanation for novice programmers.friendly_steps: concrete "what to try next" suggestions for students.traceback: optional traceback/stack text.context: correlation info (route, request_id, response_id, dom_id, phase).status_code: symbolic, HTTP-name-style status string (one ofSTATUSES); the precise failure is identified byid/category.recoverable: whether the application can continue after this error.
Status codes are a small, fixed set of symbolic strings with coarse
HTTP-like meaning (see STATUSES):
ok: Successful response.bad_request: The request itself was malformed (e.g. argument parsing failed).not_found: No matching route was found.error: A server-side failure (route execution, payload handling, bridge, system, config, or runtime error).
The fine-grained distinction between failures lives in the envelope id
(for example payload.rendering_failed) and category; the status is
only a coarse outcome bucket used for responses and telemetry display.
CATEGORIES = (CATEGORY_SYSTEM, CATEGORY_REQUEST, CATEGORY_PAYLOAD, CATEGORY_BRIDGE, CATEGORY_CONFIG, CATEGORY_RUNTIME)
module-attribute
All valid error categories.
CATEGORY_BRIDGE = 'bridge'
module-attribute
Python/JavaScript bridge failure.
CATEGORY_CONFIG = 'config'
module-attribute
Configuration failure.
CATEGORY_PAYLOAD = 'payload'
module-attribute
Payload rendering or handling failure.
CATEGORY_REQUEST = 'request'
module-attribute
Request handling failure (routing, argument parsing).
CATEGORY_RUNTIME = 'runtime'
module-attribute
Runtime failure while executing user code.
CATEGORY_SYSTEM = 'system'
module-attribute
Internal system failure.
DEFAULT_TERMINAL_ERROR_TITLE = 'Drafter could not start'
module-attribute
Title used for a terminal error whose exception carries none.
SEVERITIES = (SEVERITY_INFO, SEVERITY_WARNING, SEVERITY_ERROR, SEVERITY_CRITICAL)
module-attribute
All valid error severities.
SEVERITY_CRITICAL = 'critical'
module-attribute
Failure the application cannot recover from.
SEVERITY_ERROR = 'error'
module-attribute
Failure of the current operation.
SEVERITY_INFO = 'info'
module-attribute
Informational event, not a failure.
SEVERITY_WARNING = 'warning'
module-attribute
Recoverable problem worth surfacing.
STATUSES = (STATUS_OK, STATUS_BAD_REQUEST, STATUS_NOT_FOUND, STATUS_ERROR)
module-attribute
All valid symbolic status codes.
STATUS_BAD_REQUEST = 'bad_request'
module-attribute
The request itself was malformed (e.g. argument parsing failed).
STATUS_ERROR = 'error'
module-attribute
A server-side failure (route execution, payload, bridge, system, etc.).
STATUS_NOT_FOUND = 'not_found'
module-attribute
No matching route was found.
STATUS_OK = 'ok'
module-attribute
Successful response.
TERMINAL_WIDTH = 78
module-attribute
Column width used when printing a student-facing error to the terminal.
ErrorDetails(id, category, message, severity=SEVERITY_ERROR, details='', data=dict(), friendly_title='', friendly_message='', friendly_steps=(), traceback=None, context=Correlation(), status_code=None, recoverable=True)
dataclass
Bases: Exception
A container for details about an error, warning, or other negative event.
The envelope carries two tiers of description: message (with details
and traceback) is the technically concise, accurate tier, while
friendly_message and friendly_steps are the student-friendly tier,
normally filled in by drafter.data.error_explainer.explain. Renderers
display both tiers; empty friendly fields mean "not provided" and
renderers fall back to generic wording.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Stable, code-like identifier (example: |
category |
str
|
One of |
message |
str
|
Technically concise, accurate message. |
severity |
str
|
One of |
details |
str
|
Developer-focused free-text details. |
data |
dict[str, Any]
|
Structured, JSON-safe details (sanitized through |
friendly_title |
str
|
Short page-title-style label for the failure (empty when not provided). |
friendly_message |
str
|
Plain-language explanation for novice programmers (empty when not provided). |
friendly_steps |
tuple[str, ...]
|
Concrete "what to try next" suggestions for students (empty when not provided). |
traceback |
str | None
|
Optional traceback/stack text. |
context |
Correlation
|
Correlation context for the error. |
status_code |
str | None
|
Symbolic status string (one of |
recoverable |
bool
|
Whether the application can continue after this error. |
__post_init__()
Initialize the Exception base and validate category, severity, and status.
to_json()
Converts the ErrorDetails instance to a JSON-serializable dictionary.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dictionary representation of the envelope, with the correlation
context serialized via its own |
StudentFacingError(message, *, friendly='', steps=(), title='')
Bases: ValueError
A ValueError that carries its own student-friendly explanation.
Drafter code that raises errors students commonly trigger (bad component
arguments, invalid names, and so on) can use this class to supply the
friendly tier right at the raise site. str(exception) stays the
technically concise message; the friendly text rides along on the
friendly_message/friendly_steps attributes, which the central error
explainer (drafter.data.error_explainer.explain) picks up with the
highest precedence when the exception is wrapped into an envelope.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Technically concise, accurate message (becomes |
required |
friendly
|
str
|
Plain-language explanation for novice programmers. |
''
|
steps
|
tuple[str, ...] | list[str]
|
Concrete "what to try next" suggestions. |
()
|
title
|
str
|
Short page-title-style label for the failure. |
''
|
envelope_from_exception(exception, error_id, category, *, message=None, details='', data=None, friendly_title=None, friendly_message=None, friendly_steps=None, severity=SEVERITY_ERROR, context=None, status_code=None, recoverable=True)
Build a canonical envelope from a raised exception.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exception
|
BaseException
|
The exception being normalized. |
required |
error_id
|
str
|
Stable, code-like identifier for this failure. |
required |
category
|
str
|
One of |
required |
message
|
str | None
|
Technically concise message; defaults to |
None
|
details
|
str
|
Developer-focused free-text details. |
''
|
data
|
dict[str, Any] | None
|
Structured, JSON-safe details (sanitized on construction). |
None
|
friendly_title
|
str | None
|
Student-friendly title; when None, one is derived from the exception via the central error explainer. |
None
|
friendly_message
|
str | None
|
Student-friendly explanation; when None, one is derived from the exception via the central error explainer. |
None
|
friendly_steps
|
tuple[str, ...] | None
|
Student-friendly fix suggestions; when None, they are derived from the exception via the central error explainer. |
None
|
severity
|
str
|
One of |
SEVERITY_ERROR
|
context
|
Correlation | None
|
Correlation context. |
None
|
status_code
|
str | None
|
Symbolic status string; defaults to |
None
|
recoverable
|
bool
|
Whether the application can continue after this error. |
True
|
Returns:
| Type | Description |
|---|---|
ErrorDetails
|
An |
format_student_facing_error(error)
Lay out a student-facing error for the terminal.
Used when a StudentFacingError escapes on the host (for example, the
development server's port is in use, or the main file cannot be found)
where there is no browser error page to show it in. The friendly
explanation comes first, then the numbered steps, then the concise
technical message as a final "Details" line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
error
|
StudentFacingError
|
The error to present. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The text to print, with a leading and trailing blank line. |
Example
error = StudentFacingError(
"Port 8000 is already in use.",
title="Port already in use",
friendly="Another program is using port 8000.",
steps=("Stop the other program.", "Or choose another port."),
)
print(format_student_facing_error(error))
json_safe(value, _depth=0)
Convert an arbitrary value into a JSON-safe structure.
Scalars pass through; dicts/lists/tuples are converted recursively
(dict keys become strings); objects offering a to_json() method are
converted through it; dataclass instances become field dictionaries;
anything else (or anything nested too deeply) becomes its repr
string, so the result is always serializable and never raises.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
The value to sanitize. |
required |
_depth
|
int
|
Internal recursion depth counter. |
0
|
Returns:
| Type | Description |
|---|---|
Any
|
A structure containing only None, bool, int, float, str, list, and dict values. |