use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::error::derive::WyrdError;
#[derive(
Clone,
Debug,
PartialEq,
Eq,
Error,
Serialize,
Deserialize,
schemars::JsonSchema,
WyrdError,
strum::EnumCount,
)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(tag = "variant", content = "data", rename_all = "snake_case")]
pub enum BifrostError {
#[error("Oracle role unavailable")]
#[wyrd_error(
code = "WYRD_VALA_503_ORACLE_ROLE_UNAVAILABLE",
status = 503,
title = "Oracle role unavailable",
remediation = "Retry through an Oracle-ready query endpoint."
)]
OracleRoleUnavailable,
#[error("running query not found")]
#[wyrd_error(
code = "WYRD_VALA_404_RUNNING_QUERY_NOT_FOUND",
status = 404,
title = "Running query not found",
remediation = "Verify the request identity or list currently running queries for the authenticated tenant."
)]
RunningQueryNotFound,
#[error("running query ownership conflict")]
#[wyrd_error(
code = "WYRD_VALA_409_RUNNING_QUERY_CONFLICT",
status = 409,
title = "Running query ownership conflict",
remediation = "Do not retry cancellation automatically; investigate the contradictory Oracle owners."
)]
RunningQueryConflict,
#[error("running query control unavailable")]
#[wyrd_error(
code = "WYRD_VALA_503_RUNNING_QUERY_CONTROL_UNAVAILABLE",
status = 503,
title = "Running query control unavailable",
remediation = "Restore every current-ready Oracle lifecycle endpoint before retrying the logical operation."
)]
RunningQueryControlUnavailable,
#[error("Scribe role unavailable")]
#[wyrd_error(
code = "WYRD_VALA_503_SCRIBE_ROLE_UNAVAILABLE",
status = 503,
title = "Scribe role unavailable",
remediation = "Retry ingest through a WAL-ready Scribe endpoint."
)]
ScribeRoleUnavailable,
#[error("query admission rejected")]
#[wyrd_error(
code = "WYRD_VALA_429_QUERY_ADMISSION_REJECTED",
status = 429,
title = "Query admission rejected",
remediation = "Retry after the supplied bounded delay or reduce demand."
)]
QueryAdmissionRejected,
#[error("query queue full")]
#[wyrd_error(
code = "WYRD_VALA_429_QUERY_QUEUE_FULL",
status = 429,
title = "Query queue full",
remediation = "Retry after capacity becomes available."
)]
QueryQueueFull,
#[error("query resources exhausted")]
#[wyrd_error(
code = "WYRD_VALA_503_QUERY_RESOURCES_EXHAUSTED",
status = 503,
title = "Query resources exhausted",
remediation = "Narrow the query or add capacity; retrying unchanged will not succeed."
)]
QueryResourcesExhausted,
#[error("query memory request too large")]
#[wyrd_error(
code = "WYRD_VALA_422_QUERY_MEMORY_REQUEST_TOO_LARGE",
status = 422,
title = "Query memory request too large",
remediation = "Reduce the requested range or query memory footprint; retrying unchanged will not succeed."
)]
QueryMemoryRequestTooLarge,
#[error("query visibility unavailable")]
#[wyrd_error(
code = "WYRD_VALA_503_QUERY_VISIBILITY_UNAVAILABLE",
status = 503,
title = "Query visibility unavailable",
remediation = "Retry when the published query source is available."
)]
QueryVisibilityUnavailable,
#[error("query tenant invariant violated")]
#[wyrd_error(
code = "WYRD_VALA_500_QUERY_TENANT_INVARIANT",
status = 500,
title = "Query tenant invariant violated",
remediation = "Stop and investigate tenant isolation; never retry as caller input."
)]
QueryTenantInvariant,
#[error("query reconciliation invariant violated")]
#[wyrd_error(
code = "WYRD_VALA_500_QUERY_RECONCILIATION_INVARIANT",
status = 500,
title = "Query reconciliation invariant violated",
remediation = "Stop and investigate cross-tier row corruption."
)]
QueryReconciliationInvariant,
#[error("query peer security validation failed")]
#[wyrd_error(
code = "WYRD_VALA_403_QUERY_PEER_SECURITY",
status = 403,
title = "Query peer security validation failed",
remediation = "Reject the peer attempt and investigate credential/fence/replay state."
)]
QueryPeerSecurity,
#[error("query stream protocol violation")]
#[wyrd_error(
code = "WYRD_VALA_502_QUERY_STREAM_PROTOCOL",
status = 502,
title = "Query stream protocol violation",
remediation = "Discard partial output and retry through a healthy server."
)]
QueryStreamProtocol,
#[error("query stream incomplete")]
#[wyrd_error(
code = "WYRD_VALA_502_QUERY_STREAM_INCOMPLETE",
status = 502,
title = "Query stream incomplete",
remediation = "Discard partial output; retry because no terminal was observed."
)]
QueryStreamIncomplete,
#[error("query execution failed")]
#[wyrd_error(
code = "WYRD_VALA_500_QUERY_EXECUTION_FAILED",
status = 500,
title = "Query execution failed",
remediation = "Inspect the scrubbed terminal error and server diagnostics before retrying."
)]
QueryExecutionFailed,
#[error("ingest authentication failed: {message}")]
#[wyrd_error(
code = "WYRD_VALA_401_INGEST_AUTH",
status = 401,
title = "Ingest authentication failed",
remediation = "Provide a valid Wyrd access token with permission to write the target ingest surface."
)]
IngestAuthentication {
message: String,
},
#[error("ingest request validation failed: {message}")]
#[wyrd_error(
code = "WYRD_VALA_400_INGEST_PROTO",
status = 400,
title = "Invalid ingest request",
remediation = "Fix the ingest request fields and retry with a valid Wyrd ingest payload."
)]
IngestProtocol {
message: String,
},
#[error("schema parse failed: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_400_SCHEMA_PARSE",
status = 400,
title = "Bifrost schema parse failed",
remediation = "Correct the column type or the row value so it satisfies the table's declared DataTypeSpec; declare a Variant field for open data."
)]
SchemaParse {
detail: String,
},
#[error("invalid Variant JSON in field {field} row {row} at {path}")]
#[wyrd_error(
code = "WYRD_VALA_400_VARIANT_INVALID_JSON",
status = 400,
title = "Invalid Variant JSON",
remediation = "Supply valid JSON text or a valid arrow.parquet.variant value for the Variant field; use try_parse_json in SQL to map invalid text to null."
)]
VariantInvalidJson {
field: String,
row: u64,
path: String,
},
#[error("Variant {numeric_kind} value out of range in field {field} row {row} at {path}")]
#[wyrd_error(
code = "WYRD_VALA_400_VARIANT_NUMERIC_OUT_OF_RANGE",
status = 400,
title = "Variant numeric value out of range",
remediation = "Send integers within the signed 64-bit or Variant decimal range, or send the number as a string."
)]
VariantNumericOutOfRange {
field: String,
row: u64,
path: String,
numeric_kind: String,
},
#[error(
"Variant value in field {field} row {row} nests {depth} levels, limit {limit} at {path}"
)]
#[wyrd_error(
code = "WYRD_VALA_400_VARIANT_TOO_DEEP",
status = 400,
title = "Variant value too deep",
remediation = "Flatten the value so it nests at most 64 levels, counting the root container as one."
)]
VariantTooDeep {
field: String,
row: u64,
path: String,
depth: u32,
limit: u32,
},
#[error("Variant value in field {field} row {row} encodes {bytes} bytes, limit {limit}")]
#[wyrd_error(
code = "WYRD_VALA_413_VARIANT_TOO_LARGE",
status = 413,
title = "Variant value too large",
remediation = "Keep each Variant value at most 8388608 encoded bytes (metadata plus value); split large payloads across rows."
)]
VariantTooLarge {
field: String,
row: u64,
bytes: u64,
limit: u64,
},
#[error("undeclared field {field} in row {row}")]
#[wyrd_error(
code = "WYRD_VALA_400_BIFROST_UNDECLARED_FIELD",
status = 400,
title = "Undeclared Bifrost field",
remediation = "Remove the key or declare it on the table; store open data in a Variant field."
)]
UndeclaredField {
field: String,
row: u64,
},
#[error("unsupported type {data_type} for field {field}")]
#[wyrd_error(
code = "WYRD_VALA_400_BIFROST_UNSUPPORTED_TYPE",
status = 400,
title = "Unsupported Bifrost type",
remediation = "Use a supported Bifrost column type; store open data in a Variant field."
)]
UnsupportedType {
field: String,
data_type: String,
},
#[error("reserved system column: {column}")]
#[wyrd_error(
code = "WYRD_VALA_400_BIFROST_RESERVED_COLUMN",
status = 400,
title = "Reserved system column name",
remediation = "Rename the column — run_id, card_uid, principal_id, wyrd_request_id, and wyrd_ingested_at are reserved."
)]
ReservedColumn {
column: String,
},
#[error("write to reserved built-in table denied: {table}")]
#[wyrd_error(
code = "WYRD_VALA_403_BIFROST_RESERVED_BUILTIN_WRITE",
status = 403,
title = "Reserved built-in write denied",
remediation = "Write through the supported observation or audit API instead of directly targeting a reserved built-in table."
)]
ReservedBuiltinWriteDenied {
table: String,
},
#[error("table missing data_tenant_id managed column: {table}")]
#[wyrd_error(
code = "WYRD_VALA_400_BIFROST_TENANT_ISOLATION_COLUMN_MISSING",
status = 400,
title = "Tenant isolation column missing",
remediation = "Add the server-managed data_tenant_id Utf8 column to the physical table schema."
)]
TenantIsolationColumnMissing {
table: String,
},
#[error("no tenant binding for OLAP operation")]
#[wyrd_error(
code = "WYRD_VALA_403_BIFROST_TENANT_BINDING_MISSING",
status = 403,
title = "No tenant binding for OLAP operation",
remediation = "Acquire a TenantConn for a valid tenant before writing or querying Bifrost tables."
)]
TenantBindingMissing,
#[error("Card outside principal card scope")]
#[wyrd_error(
code = "WYRD_VALA_403_BIFROST_CARD_SCOPE",
status = 403,
title = "Card outside principal card scope",
remediation = "Attribute the write or call to a Card the authenticated principal is authorized to tag, or add the target Card to the Service card's components."
)]
CardScopeDenied,
#[error("card_ref cannot be resolved: {card_ref}")]
#[wyrd_error(
code = "WYRD_VALA_403_CARD_UNRESOLVED",
status = 403,
title = "Card reference unresolved",
remediation = "Use a card_ref that resolves to a registered card in the current tenant."
)]
CardUnresolved {
card_ref: String,
},
#[error("sub_principal_id and act_principal_id cannot be stamped for authenticated write")]
#[wyrd_error(
code = "WYRD_VALA_401_PRINCIPAL_UNRESOLVED",
status = 401,
title = "Ingest principal unresolved",
remediation = "Authenticate with a token containing a valid principal identity and retry."
)]
PrincipalUnresolved,
#[error("bifrost table not found: {table}")]
#[wyrd_error(
code = "WYRD_VALA_404_BIFROST_TABLE_NOT_FOUND",
status = 404,
title = "Bifrost table not found",
remediation = "Create the table via the catalog API before writing or querying."
)]
TableNotFound {
table: String,
},
#[error("schema fingerprint mismatch for table: {table}")]
#[wyrd_error(
code = "WYRD_VALA_409_BIFROST_FINGERPRINT_MISMATCH",
status = 409,
title = "Schema fingerprint mismatch",
remediation = "The Arrow schema does not match the registered table schema. Evolve the schema explicitly."
)]
FingerprintMismatch {
table: String,
},
#[error("concurrent commit conflict for table: {table}")]
#[wyrd_error(
code = "WYRD_VALA_409_BIFROST_COMMIT_CONFLICT",
status = 409,
title = "Concurrent OLAP commit conflict",
remediation = "Retry the write — another writer committed to the same table concurrently."
)]
CommitConflict {
table: String,
},
#[error("duplicate failed batch: {batch_id}")]
#[wyrd_error(
code = "WYRD_VALA_409_BIFROST_DUPLICATE_FAILED_BATCH",
status = 409,
title = "Duplicate batch previously failed",
remediation = "This batch_id was committed but the write failed. Use a new batch_id to retry."
)]
DuplicateFailedBatch {
batch_id: String,
},
#[error("live-tail stream mismatch: requested={requested}, actual={actual}")]
#[wyrd_error(
code = "WYRD_VALA_409_STREAM_MISMATCH",
status = 409,
title = "Live-tail stream identity mismatch",
remediation = "Refresh the Scribe stream identity from the cluster catalog and retry the tail-fetch against the current writer stream."
)]
StreamMismatch {
requested: String,
actual: String,
},
#[error("catalog metadata inconsistency: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_500_BIFROST_METADATA_MISMATCH",
status = 500,
title = "Bifrost table metadata mismatch",
remediation = "Investigate table metadata integrity; possible mid-flight schema-evolution race."
)]
MetadataMismatch {
detail: String,
},
#[error("OLAP catalog unreachable: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_503_BIFROST_CATALOG_UNREACHABLE",
status = 503,
title = "OLAP catalog unreachable",
remediation = "Check the catalog database connection and retry."
)]
CatalogUnreachable {
detail: String,
},
#[error("OLAP object storage unreachable: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_503_BIFROST_STORAGE_UNREACHABLE",
status = 503,
title = "OLAP object storage unreachable",
remediation = "Check the object storage configuration and credentials, then retry."
)]
StorageUnreachable {
detail: String,
},
#[error("writer unavailable for table: {table}")]
#[wyrd_error(
code = "WYRD_VALA_503_BIFROST_WRITER_UNAVAILABLE",
status = 503,
title = "Bifrost writer unavailable",
remediation = "The writer actor for this table is not running. Restart the write operation."
)]
WriterUnavailable {
table: String,
},
#[error("invalid or unsupported query SQL: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_400_QUERY_INVALID_SQL",
status = 400,
title = "Invalid or unsupported query SQL",
remediation = "Submit a single SELECT statement; DDL/DML and unsupported constructs are rejected."
)]
QueryInvalidSql {
detail: String,
},
#[error("query execution timed out")]
#[wyrd_error(
code = "WYRD_VALA_504_QUERY_TIMEOUT",
status = 504,
title = "Query execution timed out",
remediation = "Narrow the query by adding filters or reducing scanned partitions."
)]
QueryTimeout,
#[error("query result too large")]
#[wyrd_error(
code = "WYRD_VALA_413_QUERY_RESULT_TOO_LARGE",
status = 413,
title = "Query result too large",
remediation = "Add a LIMIT or narrower filters for large result sets."
)]
QueryResultTooLarge,
#[error("no active Bifrost table")]
#[wyrd_error(
code = "WYRD_VALA_412_NO_ACTIVE_TABLE",
status = 412,
title = "No active Bifrost table",
remediation = "Bind a table with use_table or use_table_by_name before inserting rows."
)]
NoActiveTable,
#[error("ingest payload too large: {bytes} bytes exceeds the {limit} byte limit")]
#[wyrd_error(
code = "WYRD_VALA_413_PAYLOAD_TOO_LARGE",
status = 413,
title = "Ingest payload too large",
remediation = "Reduce the request below the enforced limit reported in this problem and retry."
)]
PayloadTooLarge {
bytes: usize,
limit: usize,
},
#[error("internal bifrost failure: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_500_BIFROST_INTERNAL",
status = 500,
title = "Internal Bifrost failure",
remediation = "Check server logs for details and contact support if the issue persists."
)]
Internal {
detail: String,
},
#[error("domain table schema fingerprint drift: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_500_SCHEMA_DRIFT",
status = 500,
title = "Domain table schema fingerprint drift",
remediation = "A domain table schema changed without a coordinated migration. Restore the previous schema or run the migration runbook."
)]
SchemaDrift {
detail: String,
},
#[error("domain table physical schema drift: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_500_PHYSICAL_DRIFT",
status = 500,
title = "Domain table physical schema drift",
remediation = "The Iceberg table physical schema drifted from the declared schema. Run the repair runbook."
)]
PhysicalDrift {
detail: String,
},
#[error("domain table Iceberg table missing: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_500_ICEBERG_MISSING",
status = 500,
title = "Domain table Iceberg table missing",
remediation = "The Iceberg table for a pre-declared domain table is missing. Run the restore runbook."
)]
IcebergMissing {
detail: String,
},
#[error("write-path redaction failed: {0}")]
#[wyrd_error(
code = "WYRD_VALA_500_REDACTION_FAILED",
status = 500,
title = "Write-path redaction failed",
remediation = "The redaction classifier failed on this batch. Retry; if it persists, check the classifier installation."
)]
RedactionFailed(String),
#[error("trace not found: {trace_id}")]
#[wyrd_error(
code = "WYRD_VALA_404_TRACE_NOT_FOUND",
status = 404,
title = "Trace not found",
remediation = "Confirm the trace_id and that it falls within the queried time window."
)]
TraceNotFound {
trace_id: String,
},
#[error("query time window required")]
#[wyrd_error(
code = "WYRD_VALA_400_WINDOW_REQUIRED",
status = 400,
title = "Query time window required",
remediation = "Supply `since` and/or `until` on the query request."
)]
WindowRequired,
#[error("query filter invalid: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_400_FILTER_INVALID",
status = 400,
title = "Query filter invalid",
remediation = "Correct the filter value; see the error detail for the offending field."
)]
FilterInvalid {
detail: String,
},
#[error("page token invalid")]
#[wyrd_error(
code = "WYRD_VALA_400_PAGE_TOKEN_INVALID",
status = 400,
title = "Page token invalid",
remediation = "Restart the query from page 1 without a page_token."
)]
PageTokenInvalid,
#[error("page snapshot expired")]
#[wyrd_error(
code = "WYRD_VALA_410_PAGE_SNAPSHOT_EXPIRED",
status = 410,
title = "Page snapshot expired",
remediation = "restart the query from page 1"
)]
PageSnapshotExpired,
#[error("query forbidden")]
#[wyrd_error(
code = "WYRD_VALA_403_QUERY_FORBIDDEN",
status = 403,
title = "Query forbidden",
remediation = "The principal lacks bifrost_query:read for this table."
)]
QueryForbidden,
#[error("ingest writer busy: {table}")]
#[wyrd_error(
code = "WYRD_VALA_429_INGEST_BUSY",
status = 429,
title = "Ingest writer busy",
remediation = "The per-table ingest buffer is full. Back off and retry the full request — no rows were written."
)]
IngestBusy {
table: String,
},
#[error("WAL disk full")]
#[wyrd_error(
code = "WYRD_VALA_507_WAL_DISK_FULL",
status = 507,
title = "WAL disk full",
remediation = "The Scribe WAL disk is at capacity. Expand storage or wait for seal to free space, then retry."
)]
WalDiskFull,
#[error("OTLP request body malformed for table {table}: {detail}")]
#[wyrd_error(
code = "WYRD_VALA_400_OTLP_REQUEST_MALFORMED",
status = 400,
title = "OTLP request body malformed",
remediation = "The OTLP request body could not be decoded. Verify the Content-Type matches the encoding (application/x-protobuf or application/json) and that the payload is a valid OTLP ExportRequest."
)]
OtlpRequestMalformed {
table: String,
detail: String,
},
#[error("invalid physical layout for table {table}: {field:?}/{violation:?}")]
#[wyrd_error(
code = "WYRD_VALA_400_BIFROST_INVALID_PHYSICAL_LAYOUT",
status = 400,
title = "Invalid Bifrost physical layout",
remediation = "Declare hour or day granularity, an optional non-null timestamp time column, at most four unique schema columns as sort keys, and supported sort/null values."
)]
InvalidPhysicalLayout {
table: String,
field: PhysicalLayoutField,
violation: PhysicalLayoutViolation,
column: Option<String>,
},
#[error("physical layout mismatch for table: {table}")]
#[wyrd_error(
code = "WYRD_VALA_409_BIFROST_LAYOUT_MISMATCH",
status = 409,
title = "Bifrost physical layout mismatch",
remediation = "Retry with the table's registered physical layout or register a different table."
)]
PhysicalLayoutMismatch {
table: String,
},
#[error("invalid compaction target file size {bytes} for table {table}")]
#[wyrd_error(
code = "WYRD_VALA_400_BIFROST_INVALID_COMPACTION_TARGET",
status = 400,
title = "Invalid Bifrost compaction target",
remediation = "Omit compaction_target_file_size_bytes to follow the deployment default, or supply at least 134217728 bytes."
)]
InvalidCompactionTarget {
table: String,
bytes: u64,
},
#[error("compaction target mismatch for table: {table}")]
#[wyrd_error(
code = "WYRD_VALA_409_BIFROST_COMPACTION_TARGET_MISMATCH",
status = 409,
title = "Bifrost compaction target mismatch",
remediation = "Retry with the table's registered compaction target, omit it, or register a different table."
)]
CompactionTargetMismatch {
table: String,
},
#[error("compaction type mismatch for table: {table}")]
#[wyrd_error(
code = "WYRD_VALA_409_BIFROST_COMPACTION_TYPE_MISMATCH",
status = 409,
title = "Bifrost compaction type mismatch",
remediation = "Retry with the table's registered compaction type, omit it, or register a different table."
)]
CompactionTypeMismatch {
table: String,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(rename_all = "snake_case")]
pub enum PhysicalLayoutField {
Partition,
Granularity,
SortKey,
SortDirection,
NullOrder,
BloomColumn,
TimeColumn,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
#[serde(rename_all = "snake_case")]
pub enum PhysicalLayoutViolation {
Missing,
Duplicate,
TooManyKeys,
UnknownColumn,
UnsupportedColumn,
UnsupportedValue,
}
#[cfg(test)]
mod tests {
use super::*;
const ALL_FIELDS: [PhysicalLayoutField; 7] = [
PhysicalLayoutField::Partition,
PhysicalLayoutField::Granularity,
PhysicalLayoutField::TimeColumn,
PhysicalLayoutField::SortKey,
PhysicalLayoutField::SortDirection,
PhysicalLayoutField::NullOrder,
PhysicalLayoutField::BloomColumn,
];
const ALL_VIOLATIONS: [PhysicalLayoutViolation; 6] = [
PhysicalLayoutViolation::Missing,
PhysicalLayoutViolation::Duplicate,
PhysicalLayoutViolation::TooManyKeys,
PhysicalLayoutViolation::UnknownColumn,
PhysicalLayoutViolation::UnsupportedColumn,
PhysicalLayoutViolation::UnsupportedValue,
];
#[test]
fn physical_layout_error_contract() {
let mismatch = BifrostError::PhysicalLayoutMismatch {
table: "obs.events".to_owned(),
};
assert_eq!(mismatch.code(), "WYRD_VALA_409_BIFROST_LAYOUT_MISMATCH");
assert_eq!(mismatch.status(), 409);
for field in ALL_FIELDS {
for violation in ALL_VIOLATIONS {
for column in [None, Some("start_time_unix_nano".to_owned())] {
let error = BifrostError::InvalidPhysicalLayout {
table: "obs.events".to_owned(),
field,
violation,
column: column.clone(),
};
assert_eq!(
error.code(),
"WYRD_VALA_400_BIFROST_INVALID_PHYSICAL_LAYOUT"
);
assert_eq!(error.status(), 400);
let json = serde_json::to_string(&error).expect("layout error serializes");
let restored: BifrostError =
serde_json::from_str(&json).expect("layout error deserializes");
assert_eq!(restored, error);
assert!(
!json.contains("panic") && json.len() < 512,
"public layout error must stay bounded: {json}"
);
}
}
}
}
#[test]
fn payload_too_large_reports_the_enforced_limit() {
let error = BifrostError::PayloadTooLarge {
bytes: 41_943_040,
limit: 8_388_608,
};
assert_eq!(error.code(), "WYRD_VALA_413_PAYLOAD_TOO_LARGE");
assert_eq!(error.status(), 413);
let details = serde_json::to_value(&error).expect("payload error serializes");
assert_eq!(
details["data"]["bytes"], 41_943_040,
"measured transport bytes must survive the public projection: {details}"
);
assert_eq!(
details["data"]["limit"], 8_388_608,
"the enforced ceiling must survive the public projection: {details}"
);
let remediation = error.remediation();
assert!(
!remediation.contains("32 MiB"),
"remediation must not assert a fixed ceiling that contradicts the \
supplied limit: {remediation}"
);
assert!(
remediation.contains("limit"),
"remediation must point the caller at the supplied limit: {remediation}"
);
}
#[test]
fn physical_layout_tokens_are_snake_case() {
assert_eq!(
serde_json::to_string(&PhysicalLayoutViolation::TooManyKeys)
.expect("violation serializes"),
"\"too_many_keys\""
);
assert_eq!(
serde_json::to_string(&PhysicalLayoutField::SortDirection).expect("field serializes"),
"\"sort_direction\""
);
}
}