Skip to content

Error Types

mik-sdk provides two error types for input parsing and validation: ParseError and ValidationError.

#[allow(warnings)]
mod bindings;
use bindings::exports::mik::core::handler::{self, Guest, Response};
use mik_sdk::prelude::*;
// For direct error type access
use mik_sdk::typed::{ParseError, ValidationError};

The error types are available via mik_sdk::typed::ParseError and mik_sdk::typed::ValidationError.

Used when input cannot be parsed into the expected type.

use mik_sdk::typed::ParseError;
// Field is missing
ParseError::MissingField { field: String }
// Value format is wrong
ParseError::InvalidFormat { field: String, value: String }
// Type doesn't match
ParseError::TypeMismatch { field: String, expected: String }
// Custom error
ParseError::Custom { field: String, message: String }
let err = ParseError::missing("email");
let err = ParseError::invalid_format("age", "abc");
let err = ParseError::type_mismatch("count", "integer");
let err = ParseError::custom("field", "custom message");
let err = ParseError::missing("email");
err.field() // "email"
err.message() // "field is required"

For nested objects, add path context:

let err = ParseError::missing("city")
.with_path("address");
// err.field() => "address.city"
let nested = err.with_path("user");
// nested.field() => "user.address.city"

Used when a value exists but fails validation constraints.

use mik_sdk::typed::ValidationError;
// Below minimum
ValidationError::Min { field: String, min: i64 }
// Above maximum
ValidationError::Max { field: String, max: i64 }
// Pattern mismatch
ValidationError::Pattern { field: String, pattern: String }
// Format error
ValidationError::Format { field: String, expected: String }
// Custom constraint
ValidationError::Custom { field: String, constraint: String, message: String }
let err = ValidationError::min("name", 3);
let err = ValidationError::max("items", 100);
let err = ValidationError::pattern("email", "^.+@.+\\..+$");
let err = ValidationError::format("date", "ISO 8601");
let err = ValidationError::custom("field", "constraint_name", "error message");
let err = ValidationError::min("name", 3);
err.field() // "name"
err.constraint() // "min"
err.message() // "must be at least 3"

Both error types support pattern matching for custom handling:

fn handle_error(error: ParseError) -> Response {
match error {
ParseError::MissingField { field } => {
bad_request!(format!("{} is required", field))
}
ParseError::TypeMismatch { field, expected } => {
bad_request!(format!("{} must be a {}", field, expected))
}
ParseError::InvalidFormat { field, value } => {
bad_request!(format!("Invalid format for {}: {}", field, value))
}
_ => bad_request!("Validation failed")
}
}
fn handle_validation(error: ValidationError) -> Response {
match error {
ValidationError::Min { field, min } => {
bad_request!(format!("{} must be at least {}", field, min))
}
ValidationError::Max { field, max } => {
bad_request!(format!("{} must be at most {}", field, max))
}
_ => bad_request!("Validation failed")
}
}

ValidationError can be converted to ParseError:

fn validate_input(value: &str) -> Result<String, ParseError> {
if value.len() < 3 {
let validation_err = ValidationError::min("name", 3);
return Err(validation_err.into()); // Converts to ParseError
}
Ok(value.to_string())
}

The error types work well with Result and the ? operator:

fn parse_request(req: &Request) -> Result<UserInput, ParseError> {
let json = req.json()
.ok_or_else(|| ParseError::custom("body", "Invalid JSON"))?;
let name = json.path_str(&["name"])
.ok_or_else(|| ParseError::missing("name"))?;
if name.len() < 3 {
return Err(ValidationError::min("name", 3).into());
}
let email = json.path_str(&["email"])
.ok_or_else(|| ParseError::missing("email"))?;
Ok(UserInput { name, email })
}
fn create_user(_req: &Request) -> Response {
let body = ensure!(_req.body(), 400, "Body required");
match parse_request(body) {
Ok(input) => {
ok!({ "name": input.name })
}
Err(err) => {
error! {
status: 400,
title: "Validation Error",
detail: err.message(),
meta: { "field": err.field() }
}
}
}
}

When using typed inputs with #[derive(Type)], #[derive(Path)], or #[derive(Query)], parsing errors are automatically converted to RFC 7807 responses:

#[derive(Type)]
pub struct CreateInput {
#[field(min = 3)]
pub name: String,
pub email: String,
}
routes! {
POST "/users" => create_user(body: CreateInput),
}
// If parsing fails, automatically returns:
// {
// "type": "urn:problem:validation",
// "title": "Validation Error",
// "status": 400,
// "detail": "Missing required field",
// "errors": [
// { "field": "name", "message": "field is required" }
// ]
// }

Both error types are marked #[non_exhaustive], meaning new variants may be added in future versions. Always include a catch-all arm in match statements:

match error {
ParseError::MissingField { field } => { /* ... */ }
ParseError::TypeMismatch { field, expected } => { /* ... */ }
_ => { /* Handle unknown variants */ }
}
MethodReturnsDescription
missing(field)ParseErrorCreate MissingField
invalid_format(field, value)ParseErrorCreate InvalidFormat
type_mismatch(field, expected)ParseErrorCreate TypeMismatch
custom(field, message)ParseErrorCreate Custom
field()&strGet field name
message()StringGet error message
with_path(path)ParseErrorAdd path prefix
MethodReturnsDescription
min(field, min)ValidationErrorCreate Min
max(field, max)ValidationErrorCreate Max
pattern(field, pattern)ValidationErrorCreate Pattern
format(field, expected)ValidationErrorCreate Format
custom(field, constraint, msg)ValidationErrorCreate Custom
field()&strGet field name
constraint()&strGet constraint name
message()StringGet error message