Skip to content

Error Reference

wirespec errors fall into two categories: runtime error codes returned by generated C functions, and compiler errors emitted during compilation.


Runtime Error Codes

All generated parse and serialize functions return wirespec_result_t. The value is one of:

CodeValueMeaning
WIRESPEC_OK0Success.
WIRESPEC_ERR_SHORT_BUFFER1Not enough bytes remain in the input buffer (parse) or output buffer (serialize).
WIRESPEC_ERR_INVALID_TAG2The tag value does not match any known pattern in a frame or capsule.
WIRESPEC_ERR_CONSTRAINT3A require expression evaluated to false.
WIRESPEC_ERR_OVERFLOW4An integer overflow occurred — e.g., a VarInt value exceeds the target type range, or a length value exceeds SIZE_MAX.
WIRESPEC_ERR_INVALID_STATE5A state machine received an event for which no matching transition exists.
WIRESPEC_ERR_TRAILING_DATA6A within EXPR scope was not fully consumed — bytes remain after the inner parse completed.
WIRESPEC_ERR_NONCANONICAL7An @strict type was encoded in non-canonical (non-minimum-length) form.
WIRESPEC_ERR_UNSUPPORTED8The input requests a feature that the generated code does not support.
WIRESPEC_ERR_CAPACITY9A parsed array element count exceeds the field's allocated capacity (WIRESPEC_MAX_ARRAY_ELEMENTS or @max_len).
WIRESPEC_ERR_CHECKSUM10Checksum verification failed during parse.
WIRESPEC_ERR_SCOPE_UNDERFLOW11Sub-cursor underflow — an internal scope boundary was violated.
WIRESPEC_ERR_ARRAY_OVERFLOW12Array index out of bounds during parsing.

These codes are defined in wirespec_runtime.h:

c
typedef enum {
    WIRESPEC_OK                  = 0,
    WIRESPEC_ERR_SHORT_BUFFER    = 1,
    WIRESPEC_ERR_INVALID_TAG     = 2,
    WIRESPEC_ERR_CONSTRAINT      = 3,
    WIRESPEC_ERR_OVERFLOW        = 4,
    WIRESPEC_ERR_INVALID_STATE   = 5,
    WIRESPEC_ERR_TRAILING_DATA   = 6,
    WIRESPEC_ERR_NONCANONICAL    = 7,
    WIRESPEC_ERR_UNSUPPORTED     = 8,
    WIRESPEC_ERR_CAPACITY        = 9,
    WIRESPEC_ERR_CHECKSUM        = 10,
    WIRESPEC_ERR_SCOPE_UNDERFLOW = 11,
    WIRESPEC_ERR_ARRAY_OVERFLOW  = 12,
} wirespec_result_t;

Error Handling Pattern

c
wirespec_result_t rc = quic_frames_quic_frame_parse(buf, len, &frame, &consumed);
switch (rc) {
    case WIRESPEC_OK:
        break;
    case WIRESPEC_ERR_SHORT_BUFFER:
        /* Need more data — wait for more bytes or report truncated input */
        break;
    case WIRESPEC_ERR_INVALID_TAG:
        /* Unknown frame type — discard or log */
        break;
    case WIRESPEC_ERR_CONSTRAINT:
        /* Protocol constraint violated — close connection */
        break;
    case WIRESPEC_ERR_CHECKSUM:
        /* Corrupted packet — discard */
        break;
    default:
        /* Unexpected error */
        break;
}

Error Code Notes

WIRESPEC_ERR_SHORT_BUFFER is the most common error during incremental parsing. Callers that accumulate data in a ring buffer should retry with more data when this code is returned.

WIRESPEC_ERR_INVALID_TAG is returned only when no _ wildcard branch is present in the frame or capsule. Adding _ => Unknown { data: bytes[remaining] } causes unknown tags to be captured rather than rejected.

WIRESPEC_ERR_NONCANONICAL is returned only for types annotated with @strict. Without @strict, all encodings that decode to a valid value are accepted.

WIRESPEC_ERR_TRAILING_DATA indicates a length-prefixed scope (within EXPR) whose declared length was not fully consumed by the inner parse. This usually indicates a malformed packet or a newer protocol version with additional fields.

WIRESPEC_ERR_UNSUPPORTED indicates the input requests a feature or variant that the generated code does not handle.

WIRESPEC_ERR_CAPACITY does not indicate a protocol error — it indicates that the generated code's statically-allocated array is too small. Increase capacity with -DWIRESPEC_MAX_ARRAY_ELEMENTS=N or @max_len(N) on the field.

WIRESPEC_ERR_SCOPE_UNDERFLOW is an internal error indicating that a sub-cursor's scope boundary was violated. This typically indicates a bug in the protocol definition or an internal error in the generated code.

WIRESPEC_ERR_ARRAY_OVERFLOW indicates that an array index went out of bounds during parsing. This is a safety check in the generated code.


Compiler Errors

The wirespec compiler reports errors with source file path, line and column, a source line excerpt, a ^^^^^ pointer, context information, and optional hint text.

Error Display Format

error: undefined type 'Varnt'
 --> file.wspec:2:7
  |
2 |     x: Varnt,
  |        ^^^^^
  = in packet 'Foo'
  hint: did you mean 'VarInt'?

Fields in the display:

FieldDescription
error:Error message
-->File path, line number, column number
Source lineThe line of source text containing the error
^^^^^ pointerUnderlines the offending token(s)
= in ...Context — the enclosing definition
hint:Optional hint, including similarity-based suggestions

Common Compile-Time Errors

Undefined type or field:

error: undefined type 'AckRng'
 --> examples/quic/frames.wspec:12:15
  |
12 |     gap: AckRng,
  |          ^^^^^^
  = in packet 'AckRange'
  hint: did you mean 'AckRange'?

Caused by a typo in a type name or missing import. The compiler uses Levenshtein distance to suggest the closest known name.

Forward field reference:

error: field 'length' is used before it is declared
 --> examples/quic/frames.wspec:8:28

Fields are only visible to subsequent fields in the same scope. A field cannot reference one declared later in the same packet, frame branch, or capsule.

Non-integer-like type as length or count:

error: 'bytes[length: x]' requires an integer-like type; got 'bool'

The expressions in bytes[length: EXPR], bytes[length_or_remaining: EXPR], [T; EXPR], and within EXPR must resolve to an integer-like type (u8, u16, u32, u64, VarInt, or similar).

remaining or fill not at end of scope:

error: 'bytes[remaining]' must be the last wire field in its scope
 --> examples/quic/frames.wspec:22:5

bytes[remaining] and [T; fill] consume all remaining bytes/elements in the current scope. No further wire fields may follow them (though let bindings and require clauses may).

Circular import:

error: circular import detected: a -> b -> c -> a

wirespec does not support circular imports. Refactor shared types into a common dependency module.

Missing field initialization in state machine action:

error: field 'dst.challenge' has no default value and is not assigned in action
 --> examples/mpquic/path.wspec:18:5

Every field in the destination state that lacks a default value must be explicitly assigned in the action block. Fields with = default_value in the state declaration can be omitted.

Shadowing a reserved identifier:

error: 'remaining' is a reserved keyword and cannot be used as a field name

The identifiers bool, null, fill, remaining, in_state, all, child_state_changed, src, and dst have special meaning and cannot be used as user-defined names.

Multiple @checksum annotations:

error: at most one @checksum annotation is allowed per packet/frame branch

@checksum field type mismatch:

error: @checksum(internet) requires field type u16; got u32

Similarity Suggestions

When an undefined name is used, the compiler computes the Levenshtein distance between the unknown name and all names in scope. If a close match is found, it is shown as a hint: suggestion. This covers type names, field names, and module paths in import statements.

Context Stacks

For errors that occur inside nested constructs (e.g., inside a frame branch or inside a capsule payload), the compiler emits context information showing the enclosing definition:

error: type mismatch ...
 --> examples/quic/frames.wspec:45:20
  |
  = in field 'data' in frame branch 'Stream'
  = in frame 'QuicFrame'