Skip to main content

icydb_core/error/
mod.rs

1//! Module: error
2//!
3//! Defines the canonical runtime error taxonomy for `icydb-core`.
4//! This module owns the shared error classes, origins, details, and
5//! constructor entry points used across storage, planning, execution, and
6//! serialization boundaries.
7
8#[cfg(test)]
9mod tests;
10
11use candid::CandidType;
12use icydb_diagnostic_code as diagnostic_code;
13use serde::Deserialize;
14use std::fmt;
15
16pub(crate) const COMPACT_QUERY_DIAGNOSTIC_MESSAGE: &str = "query diagnostic";
17const COMPACT_RUNTIME_DIAGNOSTIC_MESSAGE: &str = "runtime diagnostic";
18const COMPACT_STORE_DIAGNOSTIC_MESSAGE: &str = "store diagnostic";
19const COMPACT_INDEX_DIAGNOSTIC_MESSAGE: &str = "index diagnostic";
20const COMPACT_SERIALIZE_DIAGNOSTIC_MESSAGE: &str = "serialize diagnostic";
21const COMPACT_IDENTITY_DIAGNOSTIC_MESSAGE: &str = "identity diagnostic";
22
23const fn compact_message_for(_class: ErrorClass, origin: ErrorOrigin) -> &'static str {
24    match origin {
25        ErrorOrigin::Serialize => COMPACT_SERIALIZE_DIAGNOSTIC_MESSAGE,
26        ErrorOrigin::Store => COMPACT_STORE_DIAGNOSTIC_MESSAGE,
27        ErrorOrigin::Index => COMPACT_INDEX_DIAGNOSTIC_MESSAGE,
28        ErrorOrigin::Identity => COMPACT_IDENTITY_DIAGNOSTIC_MESSAGE,
29        ErrorOrigin::Query | ErrorOrigin::Planner | ErrorOrigin::Response => {
30            COMPACT_QUERY_DIAGNOSTIC_MESSAGE
31        }
32        ErrorOrigin::Cursor
33        | ErrorOrigin::Recovery
34        | ErrorOrigin::Executor
35        | ErrorOrigin::Interface => COMPACT_RUNTIME_DIAGNOSTIC_MESSAGE,
36    }
37}
38
39// ============================================================================
40// INTERNAL ERROR TAXONOMY — ARCHITECTURAL CONTRACT
41// ============================================================================
42//
43// This file defines the canonical runtime error classification system for
44// icydb-core. It is the single source of truth for:
45//
46//   • ErrorClass   (semantic domain)
47//   • ErrorOrigin  (subsystem boundary)
48//   • Structured detail payloads
49//   • Canonical constructor entry points
50//
51// -----------------------------------------------------------------------------
52// DESIGN INTENT
53// -----------------------------------------------------------------------------
54//
55// 1. InternalError is a *taxonomy carrier*, not a formatting utility.
56//
57//    - ErrorClass represents semantic meaning (corruption, invariant_violation,
58//      unsupported, etc).
59//    - ErrorOrigin represents the subsystem boundary (store, index, query,
60//      executor, serialize, interface, etc).
61//    - The (class, origin) pair must remain stable and intentional.
62//
63// 2. Call sites MUST prefer canonical constructors.
64//
65//    Do NOT construct errors manually via:
66//        InternalError::new(class, origin)
67//    unless you are defining a new canonical helper here.
68//
69//    If a pattern appears more than once, centralize it here.
70//
71// 3. Constructors in this file must represent real architectural boundaries.
72//
73//    Add a new helper ONLY if it:
74//
75//      • Encodes a cross-cutting invariant,
76//      • Represents a subsystem boundary,
77//      • Or prevents taxonomy drift across call sites.
78//
79//    Do NOT add feature-specific helpers.
80//    Do NOT add one-off formatting helpers.
81//    Do NOT turn this file into a generic message factory.
82//
83// 4. ErrorDetail must align with ErrorOrigin.
84//
85//    If detail is present, it MUST correspond to the origin.
86//    Do not attach mismatched detail variants.
87//
88// 5. Plan-layer errors are NOT runtime failures.
89//
90//    PlanError and CursorPlanError must be translated into
91//    executor/query invariants via the canonical mapping functions.
92//    Do not leak plan-layer error types across execution boundaries.
93//
94// 6. Preserve taxonomy stability.
95//
96//    Do NOT:
97//      • Merge error classes.
98//      • Reclassify corruption as internal.
99//      • Downgrade invariant violations.
100//      • Introduce ambiguous class/origin combinations.
101//
102//    Any change to ErrorClass or ErrorOrigin is an architectural change
103//    and must be reviewed accordingly.
104//
105// -----------------------------------------------------------------------------
106// NON-GOALS
107// -----------------------------------------------------------------------------
108//
109// This is NOT:
110//
111//   • A public API contract.
112//   • A generic error abstraction layer.
113//   • A feature-specific message builder.
114//   • A dumping ground for temporary error conversions.
115//
116// -----------------------------------------------------------------------------
117// MAINTENANCE GUIDELINES
118// -----------------------------------------------------------------------------
119//
120// When modifying this file:
121//
122//   1. Ensure classification semantics remain consistent.
123//   2. Avoid constructor proliferation.
124//   3. Prefer narrow, origin-specific helpers over ad-hoc new(...).
125//   4. Keep formatting minimal and standardized.
126//   5. Keep this file boring and stable.
127//
128// If this file grows rapidly, something is wrong at the call sites.
129//
130// ============================================================================
131
132///
133/// InternalError
134///
135/// Structured runtime error with a stable internal classification.
136/// Not a stable API; intended for internal use and may change without notice.
137///
138
139pub struct InternalError {
140    pub(crate) class: ErrorClass,
141    pub(crate) origin: ErrorOrigin,
142
143    /// Optional structured error detail.
144    /// The variant (if present) must correspond to `origin`.
145    pub(crate) detail: Option<ErrorDetail>,
146}
147
148#[expect(
149    clippy::missing_const_for_fn,
150    reason = "internal error constructors stay non-const so compact diagnostic construction does not force const churn across subsystem helper seams"
151)]
152impl InternalError {
153    /// Construct an InternalError with optional origin-specific detail.
154    /// This constructor provides default StoreError details for certain
155    /// (class, origin) combinations but does not guarantee a detail payload.
156    #[must_use]
157    #[cold]
158    #[inline(never)]
159    pub fn new(class: ErrorClass, origin: ErrorOrigin) -> Self {
160        let detail = match (class, origin) {
161            (ErrorClass::Corruption, ErrorOrigin::Store) => {
162                Some(ErrorDetail::Store(StoreError::Corrupt))
163            }
164            (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
165                Some(ErrorDetail::Store(StoreError::InvariantViolation))
166            }
167            _ => None,
168        };
169
170        Self {
171            class,
172            origin,
173            detail,
174        }
175    }
176
177    /// Return the internal error class taxonomy.
178    #[must_use]
179    pub const fn class(&self) -> ErrorClass {
180        self.class
181    }
182
183    /// Return the internal error origin taxonomy.
184    #[must_use]
185    pub const fn origin(&self) -> ErrorOrigin {
186        self.origin
187    }
188
189    /// Return the rendered internal error message.
190    #[must_use]
191    pub const fn message(&self) -> &'static str {
192        compact_message_for(self.class, self.origin)
193    }
194
195    /// Return the optional structured detail payload.
196    #[must_use]
197    pub const fn detail(&self) -> Option<&ErrorDetail> {
198        self.detail.as_ref()
199    }
200
201    /// Borrow the accepted constraint diagnostic carried by this error.
202    #[must_use]
203    pub fn constraint_diagnostic(&self) -> Option<&ConstraintDiagnostic> {
204        match self.detail.as_ref() {
205            Some(ErrorDetail::Executor(detail)) => detail.constraint_diagnostic(),
206            Some(
207                ErrorDetail::Store(_)
208                | ErrorDetail::Query(_)
209                | ErrorDetail::Recovery(_)
210                | ErrorDetail::Serialize(_),
211            )
212            | None => None,
213        }
214    }
215
216    /// Return compact diagnostic identity for this internal error.
217    #[must_use]
218    pub fn diagnostic(&self) -> diagnostic_code::Diagnostic {
219        diagnostic_code::Diagnostic::new(
220            self.diagnostic_code(),
221            self.origin.diagnostic_origin(),
222            self.detail
223                .as_ref()
224                .and_then(ErrorDetail::diagnostic_detail),
225        )
226    }
227
228    /// Return the compact diagnostic code for this internal error.
229    #[must_use]
230    pub fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
231        self.detail.as_ref().map_or_else(
232            || self.class.diagnostic_code(self.origin),
233            ErrorDetail::diagnostic_code,
234        )
235    }
236
237    /// Consume and return the rendered internal error message.
238    #[must_use]
239    pub fn into_message(self) -> String {
240        self.message().to_string()
241    }
242
243    /// Construct an error while preserving an explicit class/origin taxonomy pair.
244    #[cold]
245    #[inline(never)]
246    pub(crate) fn classified(class: ErrorClass, origin: ErrorOrigin) -> Self {
247        Self::new(class, origin)
248    }
249
250    /// Rebuild this error with a new origin while preserving class taxonomy.
251    ///
252    /// Origin-scoped detail payloads are intentionally dropped when re-origining.
253    #[cold]
254    #[inline(never)]
255    pub(crate) fn with_origin(self, origin: ErrorOrigin) -> Self {
256        Self::classified(self.class, origin)
257    }
258
259    /// Construct an index-origin invariant violation.
260    #[cold]
261    #[inline(never)]
262    pub(crate) fn index_invariant() -> Self {
263        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Index)
264    }
265
266    /// Construct the canonical index field-count invariant for key building.
267    pub(crate) fn index_key_field_count_exceeds_max(
268        _index_name: &str,
269        _field_count: usize,
270        _max_fields: usize,
271    ) -> Self {
272        Self::index_invariant()
273    }
274
275    /// Construct the canonical index-expression source-type mismatch invariant.
276    pub(crate) fn index_expression_source_type_mismatch(
277        _index_name: &str,
278        _expression: impl Sized,
279        _expected: impl Sized,
280        _source_label: &str,
281    ) -> Self {
282        Self::index_invariant()
283    }
284
285    /// Construct a planner-origin invariant violation for executor-boundary
286    /// contract drift.
287    #[cold]
288    #[inline(never)]
289    #[cfg(any(test, feature = "query"))]
290    pub(crate) fn planner_executor_invariant() -> Self {
291        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Planner)
292    }
293
294    /// Construct a query-origin invariant violation for executor-boundary
295    /// contract drift.
296    #[cold]
297    #[inline(never)]
298    pub(crate) fn query_executor_invariant() -> Self {
299        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Query)
300    }
301
302    /// Construct a cursor-origin invariant violation for executor-boundary
303    /// contract drift.
304    #[cold]
305    #[inline(never)]
306    #[cfg(any(test, feature = "query"))]
307    pub(crate) fn cursor_executor_invariant() -> Self {
308        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Cursor)
309    }
310
311    /// Construct an executor-origin invariant violation.
312    #[cold]
313    #[inline(never)]
314    pub(crate) fn executor_invariant() -> Self {
315        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Executor)
316    }
317
318    /// Construct an executor-origin conflict.
319    #[cold]
320    #[inline(never)]
321    pub(crate) fn executor_conflict() -> Self {
322        Self::new(ErrorClass::Conflict, ErrorOrigin::Executor)
323    }
324
325    /// Construct an executor-origin internal error.
326    #[cold]
327    #[inline(never)]
328    pub(crate) fn executor_internal() -> Self {
329        Self::new(ErrorClass::Internal, ErrorOrigin::Executor)
330    }
331
332    /// Construct an executor-origin unsupported error.
333    #[cold]
334    #[inline(never)]
335    pub(crate) fn executor_unsupported() -> Self {
336        Self::new(ErrorClass::Unsupported, ErrorOrigin::Executor)
337    }
338
339    /// Construct an executor-origin database-owned-field authorship rejection.
340    pub(crate) fn mutation_database_owned_field_explicit(
341        _entity_path: &str,
342        _field_name: &str,
343    ) -> Self {
344        Self {
345            class: ErrorClass::Unsupported,
346            origin: ErrorOrigin::Executor,
347            detail: Some(ErrorDetail::Executor(
348                ExecutorErrorDetail::MutationDatabaseOwnedFieldExplicit,
349            )),
350        }
351    }
352
353    /// Construct an executor-origin required-field omission rejection.
354    #[must_use]
355    pub fn mutation_required_field_missing(_entity_path: &str, _field_names: &str) -> Self {
356        Self {
357            class: ErrorClass::Unsupported,
358            origin: ErrorOrigin::Executor,
359            detail: Some(ErrorDetail::Executor(
360                ExecutorErrorDetail::MutationRequiredFieldMissing,
361            )),
362        }
363    }
364
365    /// Construct an executor-origin managed-timestamp clock regression.
366    #[must_use]
367    pub(crate) fn mutation_managed_timestamp_regression() -> Self {
368        Self {
369            class: ErrorClass::InvariantViolation,
370            origin: ErrorOrigin::Executor,
371            detail: Some(ErrorDetail::Executor(
372                ExecutorErrorDetail::MutationManagedTimestampRegression,
373            )),
374        }
375    }
376
377    /// Construct an executor-origin accepted constraint or activation-gate violation.
378    pub(crate) fn mutation_constraint_violation(diagnostic: ConstraintDiagnostic) -> Self {
379        Self {
380            class: ErrorClass::InvariantViolation,
381            origin: ErrorOrigin::Executor,
382            detail: Some(ErrorDetail::Executor(
383                ExecutorErrorDetail::ConstraintViolation {
384                    diagnostic: Box::new(diagnostic),
385                },
386            )),
387        }
388    }
389
390    /// Construct an executor-origin corruption failure for row-constraint authority.
391    pub(crate) fn accepted_row_constraint_program_corrupt() -> Self {
392        Self {
393            class: ErrorClass::Corruption,
394            origin: ErrorOrigin::Executor,
395            detail: Some(ErrorDetail::Executor(
396                ExecutorErrorDetail::AcceptedRowConstraintProgramCorrupt,
397            )),
398        }
399    }
400
401    /// Construct one typed migration conflict for an incomplete activation gate.
402    pub(crate) fn mutation_constraint_activation_write_blocked(
403        diagnostic: ConstraintDiagnostic,
404    ) -> Self {
405        Self {
406            class: ErrorClass::Conflict,
407            origin: ErrorOrigin::Executor,
408            detail: Some(ErrorDetail::Executor(
409                ExecutorErrorDetail::ConstraintActivationWriteBlocked {
410                    diagnostic: Box::new(diagnostic),
411                },
412            )),
413        }
414    }
415
416    /// Construct an executor-origin mutation unknown-field invariant.
417    pub(crate) fn mutation_structural_field_unknown(_entity_path: &str, _field_name: &str) -> Self {
418        Self::executor_invariant()
419    }
420
421    /// Construct a query-origin scalar page invariant for missing order at the cursor boundary.
422    #[cfg(any(test, feature = "query"))]
423    pub(crate) fn scalar_page_cursor_boundary_order_required() -> Self {
424        Self::query_executor_invariant()
425    }
426
427    /// Construct a query-origin scalar page invariant for cursor-before-ordering drift.
428    #[cfg(any(test, feature = "query"))]
429    pub(crate) fn scalar_page_cursor_boundary_after_ordering_required() -> Self {
430        Self::query_executor_invariant()
431    }
432
433    /// Construct a query-origin scalar page invariant for pagination-before-ordering drift.
434    #[cfg(any(test, feature = "query"))]
435    pub(crate) fn scalar_page_pagination_after_ordering_required() -> Self {
436        Self::query_executor_invariant()
437    }
438
439    /// Construct a query-origin fast-stream invariant for route kind/request mismatch.
440    #[cfg(any(test, feature = "query"))]
441    pub(crate) fn fast_stream_route_kind_request_match_required() -> Self {
442        Self::query_executor_invariant()
443    }
444
445    /// Construct a query-origin scan invariant for missing index-prefix executable specs.
446    #[cfg(any(test, feature = "query"))]
447    pub(crate) fn secondary_index_prefix_spec_required() -> Self {
448        Self::query_executor_invariant()
449    }
450
451    /// Construct a query-origin scan invariant for missing index-range executable specs.
452    #[cfg(any(test, feature = "query"))]
453    pub(crate) fn index_range_limit_spec_required() -> Self {
454        Self::query_executor_invariant()
455    }
456
457    /// Construct an executor-origin mutation conflict for duplicate atomic save keys.
458    pub(crate) fn mutation_atomic_save_duplicate_key(_entity_path: &str, _key: impl Sized) -> Self {
459        Self::executor_conflict()
460    }
461
462    /// Construct an executor-origin mutation invariant for index-store generation drift.
463    pub(crate) fn mutation_index_store_generation_changed(
464        _expected_generation: u64,
465        _observed_generation: u64,
466    ) -> Self {
467        Self::executor_invariant()
468    }
469
470    /// Construct a planner-origin invariant violation.
471    #[cold]
472    #[inline(never)]
473    #[cfg(any(test, feature = "query"))]
474    pub(crate) fn planner_invariant() -> Self {
475        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Planner)
476    }
477
478    /// Construct a planner-origin invalid-logical-plan invariant.
479    #[cfg(any(test, feature = "query"))]
480    pub(crate) fn query_invalid_logical_plan() -> Self {
481        Self::planner_invariant()
482    }
483
484    /// Construct a store-origin invariant violation.
485    pub(crate) fn store_invariant() -> Self {
486        Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Store)
487    }
488
489    /// Construct a store-origin internal error.
490    #[cold]
491    #[inline(never)]
492    pub(crate) fn store_internal() -> Self {
493        Self::new(ErrorClass::Internal, ErrorOrigin::Store)
494    }
495
496    /// Construct the canonical unconfigured commit-memory id internal error.
497    pub(crate) fn commit_memory_id_unconfigured() -> Self {
498        Self::store_internal()
499    }
500
501    /// Construct the canonical initialized commit-store lookup invariant.
502    pub(crate) fn commit_store_uninitialized() -> Self {
503        Self::store_invariant()
504    }
505
506    /// Construct the canonical commit-memory id mismatch internal error.
507    pub(crate) fn commit_memory_id_mismatch(_cached_id: u8, _configured_id: u8) -> Self {
508        Self::store_internal()
509    }
510
511    /// Construct the canonical commit-memory stable-key mismatch internal error.
512    pub(crate) fn commit_memory_stable_key_mismatch(
513        _cached_key: &str,
514        _configured_key: &str,
515    ) -> Self {
516        Self::store_internal()
517    }
518
519    /// Construct the canonical database-incarnation generation failure.
520    pub(crate) fn database_incarnation_generation_failed() -> Self {
521        Self::store_internal()
522    }
523
524    /// Construct the canonical zero database-incarnation corruption error.
525    pub(crate) fn database_incarnation_invalid() -> Self {
526        Self::store_corruption()
527    }
528
529    /// Construct a recovery-origin incompatible store-format error.
530    pub(crate) fn recovery_unsupported_database_format(found: Option<u16>, required: u16) -> Self {
531        Self {
532            class: ErrorClass::IncompatiblePersistedFormat,
533            origin: ErrorOrigin::Recovery,
534            detail: Some(ErrorDetail::Recovery(
535                RecoveryErrorDetail::UnsupportedFormatVersion { found, required },
536            )),
537        }
538    }
539
540    /// Construct a recovery-origin malformed store-format marker error.
541    pub(crate) fn recovery_malformed_database_format_marker(
542        reason: RecoveryFormatMarkerError,
543    ) -> Self {
544        Self {
545            class: ErrorClass::Corruption,
546            origin: ErrorOrigin::Recovery,
547            detail: Some(ErrorDetail::Recovery(
548                RecoveryErrorDetail::MalformedFormatMarker { reason },
549            )),
550        }
551    }
552
553    /// Construct a recovery-origin boot control-memory failure.
554    pub(crate) fn recovery_database_format_control_unavailable() -> Self {
555        Self::new(ErrorClass::Internal, ErrorOrigin::Recovery)
556    }
557
558    /// Construct a commit control-memory growth failure.
559    pub(crate) fn commit_control_memory_growth_failed() -> Self {
560        Self::store_internal()
561    }
562
563    /// Construct a store-format memory registration failure.
564    #[cfg(not(test))]
565    pub(crate) fn database_format_memory_registration_failed(_err: impl Sized) -> Self {
566        Self::store_internal()
567    }
568
569    /// Construct the canonical recovered-effect verification failure.
570    pub(crate) fn recovery_effect_verification_failed() -> Self {
571        Self::store_corruption()
572    }
573
574    /// Construct an index-origin internal error.
575    #[cold]
576    #[inline(never)]
577    pub(crate) fn index_internal() -> Self {
578        Self::new(ErrorClass::Internal, ErrorOrigin::Index)
579    }
580
581    /// Construct the canonical missing old entity-key internal error for structural index removal.
582    pub(crate) fn structural_index_removal_entity_key_required() -> Self {
583        Self::index_internal()
584    }
585
586    /// Construct the canonical missing new entity-key internal error for structural index insertion.
587    pub(crate) fn structural_index_insertion_entity_key_required() -> Self {
588        Self::index_internal()
589    }
590
591    /// Construct the canonical missing old entity-key internal error for index commit-op removal.
592    pub(crate) fn index_commit_op_old_entity_key_required() -> Self {
593        Self::index_internal()
594    }
595
596    /// Construct the canonical missing new entity-key internal error for index commit-op insertion.
597    pub(crate) fn index_commit_op_new_entity_key_required() -> Self {
598        Self::index_internal()
599    }
600
601    /// Construct a query-origin internal error.
602    #[cfg(test)]
603    pub(crate) fn query_internal() -> Self {
604        Self::new(ErrorClass::Internal, ErrorOrigin::Query)
605    }
606
607    /// Construct a query-origin unsupported error.
608    #[cold]
609    #[inline(never)]
610    #[cfg(any(test, feature = "query"))]
611    pub(crate) fn query_unsupported() -> Self {
612        Self::new(ErrorClass::Unsupported, ErrorOrigin::Query)
613    }
614
615    /// Construct a query-origin conflict for execution against a superseded
616    /// accepted schema revision.
617    #[cold]
618    #[inline(never)]
619    #[cfg(any(test, feature = "query"))]
620    pub(crate) fn query_stale_accepted_schema_revision(
621        _expected_revision: u64,
622        _current_revision: Option<u64>,
623    ) -> Self {
624        Self {
625            class: ErrorClass::Conflict,
626            origin: ErrorOrigin::Query,
627            detail: Some(ErrorDetail::Query(QueryErrorDetail::StaleSchemaRevision)),
628        }
629    }
630
631    /// Construct a query-origin SQL DDL admission error with structured detail.
632    #[cold]
633    #[inline(never)]
634    #[cfg(feature = "sql")]
635    pub(crate) fn query_schema_ddl_admission(error: SchemaDdlAdmissionError) -> Self {
636        Self {
637            class: ErrorClass::Unsupported,
638            origin: ErrorOrigin::Query,
639            detail: Some(ErrorDetail::Query(QueryErrorDetail::SchemaDdlAdmission {
640                error,
641            })),
642        }
643    }
644
645    /// Construct a query-origin numeric overflow error with structured detail.
646    #[cold]
647    #[inline(never)]
648    #[cfg(any(test, feature = "query"))]
649    pub(crate) fn query_numeric_overflow() -> Self {
650        Self {
651            class: ErrorClass::Unsupported,
652            origin: ErrorOrigin::Query,
653            detail: Some(ErrorDetail::Query(QueryErrorDetail::NumericOverflow)),
654        }
655    }
656
657    /// Construct a query-origin non-representable numeric result error with
658    /// structured detail.
659    #[cold]
660    #[inline(never)]
661    #[cfg(any(test, feature = "query"))]
662    pub(crate) fn query_numeric_not_representable() -> Self {
663        Self {
664            class: ErrorClass::Unsupported,
665            origin: ErrorOrigin::Query,
666            detail: Some(ErrorDetail::Query(
667                QueryErrorDetail::NumericNotRepresentable,
668            )),
669        }
670    }
671
672    /// Construct a serialize-origin internal error.
673    #[cold]
674    #[inline(never)]
675    pub(crate) fn serialize_internal() -> Self {
676        Self::new(ErrorClass::Internal, ErrorOrigin::Serialize)
677    }
678
679    /// Construct the canonical persisted-row encode internal error.
680    pub(crate) fn persisted_row_encode_failed(_detail: impl Sized) -> Self {
681        Self::persisted_row_encode_internal()
682    }
683
684    /// Construct the compact persisted-row encode internal error.
685    pub(crate) fn persisted_row_encode_internal() -> Self {
686        Self::serialize_internal()
687    }
688
689    /// Construct the compact persisted-row field encode internal error.
690    pub(crate) fn persisted_row_field_encode_internal(_field_name: &str) -> Self {
691        Self::persisted_row_encode_internal()
692    }
693
694    /// Construct a store-origin corruption error.
695    #[cold]
696    #[inline(never)]
697    pub(crate) fn store_corruption() -> Self {
698        Self::new(ErrorClass::Corruption, ErrorOrigin::Store)
699    }
700
701    /// Construct a store-origin commit-marker corruption error.
702    pub(crate) fn commit_corruption() -> Self {
703        Self::store_corruption()
704    }
705
706    /// Construct a store-origin commit-marker component corruption error.
707    pub(crate) fn commit_component_corruption() -> Self {
708        Self::commit_corruption()
709    }
710
711    /// Construct the canonical commit-marker id generation internal error.
712    pub(crate) fn commit_id_generation_failed() -> Self {
713        Self::store_internal()
714    }
715
716    /// Construct the canonical commit-marker payload u32-length-limit error.
717    pub(crate) fn commit_marker_payload_exceeds_u32_length_limit() -> Self {
718        Self::store_unsupported()
719    }
720
721    /// Construct the canonical commit-marker component invalid-length corruption error.
722    pub(crate) fn commit_component_length_invalid() -> Self {
723        Self::commit_corruption()
724    }
725
726    /// Construct the canonical commit-marker max-size corruption error.
727    pub(crate) fn commit_marker_exceeds_max_size() -> Self {
728        Self::commit_corruption()
729    }
730
731    /// Construct the canonical commit-control slot max-size unsupported error.
732    pub(crate) fn commit_control_slot_exceeds_max_size() -> Self {
733        Self::store_unsupported()
734    }
735
736    /// Construct the canonical commit-control marker-bytes length-limit error.
737    pub(crate) fn commit_control_slot_marker_bytes_exceed_u32_length_limit() -> Self {
738        Self::store_unsupported()
739    }
740
741    /// Construct the canonical startup index-rebuild invalid-data-key corruption error.
742    pub(crate) fn startup_index_rebuild_invalid_data_key() -> Self {
743        Self::store_corruption()
744    }
745
746    /// Construct an index-origin corruption error.
747    #[cold]
748    #[inline(never)]
749    pub(crate) fn index_corruption() -> Self {
750        Self::new(ErrorClass::Corruption, ErrorOrigin::Index)
751    }
752
753    /// Construct the canonical unique-validation corruption wrapper.
754    pub(crate) fn index_unique_validation_corruption() -> Self {
755        Self::index_plan_index_corruption()
756    }
757
758    /// Construct the canonical structural index-entry corruption wrapper.
759    pub(crate) fn structural_index_entry_corruption() -> Self {
760        Self::index_plan_index_corruption()
761    }
762
763    /// Construct the canonical missing new entity-key invariant during unique validation.
764    pub(crate) fn index_unique_validation_entity_key_required() -> Self {
765        Self::index_invariant()
766    }
767
768    /// Construct the canonical unique-validation structural row-decode corruption error.
769    pub(crate) fn index_unique_validation_row_deserialize_failed() -> Self {
770        Self::index_plan_serialize_corruption()
771    }
772
773    /// Construct the canonical unique-validation primary-key slot decode corruption error.
774    pub(crate) fn index_unique_validation_primary_key_decode_failed() -> Self {
775        Self::index_plan_serialize_corruption()
776    }
777
778    /// Construct the canonical unique-validation stored key rebuild corruption error.
779    pub(crate) fn index_unique_validation_key_rebuild_failed() -> Self {
780        Self::index_plan_serialize_corruption()
781    }
782
783    /// Construct the canonical unique-validation missing-row corruption error.
784    pub(crate) fn index_unique_validation_row_required() -> Self {
785        Self::index_plan_store_corruption()
786    }
787
788    /// Construct the canonical index-only predicate missing-component invariant.
789    #[cfg(any(test, feature = "query"))]
790    pub(crate) fn index_only_predicate_component_required() -> Self {
791        Self::index_invariant()
792    }
793
794    /// Construct the canonical index-scan continuation-envelope invariant.
795    #[cfg(any(test, feature = "query"))]
796    pub(crate) fn index_scan_continuation_anchor_within_envelope_required() -> Self {
797        Self::index_invariant()
798    }
799
800    /// Construct the canonical index-scan continuation-advancement invariant.
801    #[cfg(any(test, feature = "query"))]
802    pub(crate) fn index_scan_continuation_advancement_required() -> Self {
803        Self::index_invariant()
804    }
805
806    /// Construct the canonical index-scan key-decode corruption error.
807    #[cfg(any(test, feature = "query"))]
808    pub(crate) fn index_scan_key_corrupted_during(
809        _context: &'static str,
810        _err: impl Sized,
811    ) -> Self {
812        Self::index_corruption()
813    }
814
815    /// Construct the canonical index-scan missing projection-component invariant.
816    #[cfg(any(test, feature = "query"))]
817    pub(crate) fn index_projection_component_required(
818        _index_name: &str,
819        _component_index: usize,
820    ) -> Self {
821        Self::index_invariant()
822    }
823
824    /// Construct the canonical scan-time index-entry decode corruption error.
825    #[cfg(any(test, feature = "query"))]
826    pub(crate) fn index_entry_decode_failed() -> Self {
827        Self::index_corruption()
828    }
829
830    /// Construct a serialize-origin corruption error.
831    pub(crate) fn serialize_corruption() -> Self {
832        Self::new(ErrorClass::Corruption, ErrorOrigin::Serialize)
833    }
834
835    /// Construct the compact persisted-row decode corruption error.
836    pub(crate) fn persisted_row_decode_corruption() -> Self {
837        Self::serialize_corruption()
838    }
839
840    /// Construct a persisted-row layout-window corruption error.
841    pub(crate) fn persisted_row_layout_outside_accepted_window() -> Self {
842        Self {
843            class: ErrorClass::Corruption,
844            origin: ErrorOrigin::Serialize,
845            detail: Some(ErrorDetail::Serialize(
846                SerializeErrorDetail::PersistedRowLayoutOutsideAcceptedWindow,
847            )),
848        }
849    }
850
851    /// Construct a persisted-row stamped-layout slot-count corruption error.
852    pub(crate) fn persisted_row_slot_count_mismatch() -> Self {
853        Self {
854            class: ErrorClass::Corruption,
855            origin: ErrorOrigin::Serialize,
856            detail: Some(ErrorDetail::Serialize(
857                SerializeErrorDetail::PersistedRowSlotCountMismatch,
858            )),
859        }
860    }
861
862    /// Construct the canonical persisted-row field decode corruption error.
863    pub(crate) fn persisted_row_field_decode_failed(field_name: &str, _detail: impl Sized) -> Self {
864        Self::persisted_row_field_decode_corruption(field_name)
865    }
866
867    /// Construct the compact persisted-row field decode corruption error.
868    pub(crate) fn persisted_row_field_decode_corruption(_field_name: &str) -> Self {
869        Self::persisted_row_decode_corruption()
870    }
871
872    /// Construct the canonical persisted-row field-kind decode corruption error.
873    pub(crate) fn persisted_row_field_kind_decode_failed(
874        field_name: &str,
875        _field_kind: impl fmt::Debug,
876        _detail: impl Sized,
877    ) -> Self {
878        Self::persisted_row_field_decode_corruption(field_name)
879    }
880
881    /// Construct the canonical persisted-row scalar-payload length corruption error.
882    pub(crate) fn persisted_row_field_payload_exact_len_required(field_name: &str) -> Self {
883        Self::persisted_row_field_decode_corruption(field_name)
884    }
885
886    /// Construct the canonical persisted-row scalar-payload empty-body corruption error.
887    pub(crate) fn persisted_row_field_payload_must_be_empty(field_name: &str) -> Self {
888        Self::persisted_row_field_decode_corruption(field_name)
889    }
890
891    /// Construct the canonical persisted-row scalar-payload invalid-byte corruption error.
892    pub(crate) fn persisted_row_field_payload_invalid_byte(field_name: &str) -> Self {
893        Self::persisted_row_field_decode_corruption(field_name)
894    }
895
896    /// Construct the canonical persisted-row scalar-payload non-finite corruption error.
897    pub(crate) fn persisted_row_field_payload_non_finite(field_name: &str) -> Self {
898        Self::persisted_row_field_decode_corruption(field_name)
899    }
900
901    /// Construct the canonical persisted-row invalid text payload corruption error.
902    pub(crate) fn persisted_row_field_text_payload_invalid_utf8(field_name: &str) -> Self {
903        Self::persisted_row_field_decode_corruption(field_name)
904    }
905
906    /// Construct the canonical persisted-row structural slot-lookup invariant.
907    pub(crate) fn persisted_row_slot_lookup_out_of_bounds(_model_path: &str, _slot: usize) -> Self {
908        Self::index_invariant()
909    }
910
911    /// Construct the canonical persisted-row structural slot-cache invariant.
912    pub(crate) fn persisted_row_slot_cache_lookup_out_of_bounds(
913        _model_path: &str,
914        _slot: usize,
915    ) -> Self {
916        Self::index_invariant()
917    }
918
919    /// Construct the canonical persisted-row primary-key decode corruption error.
920    pub(crate) fn persisted_row_primary_key_not_primary_key_encodable(
921        _data_key: impl fmt::Debug,
922        _detail: impl Sized,
923    ) -> Self {
924        Self::persisted_row_decode_corruption()
925    }
926
927    /// Construct the canonical persisted-row missing primary-key slot corruption error.
928    pub(crate) fn persisted_row_primary_key_slot_missing(_data_key: impl fmt::Debug) -> Self {
929        Self::persisted_row_decode_corruption()
930    }
931
932    /// Construct the canonical persisted-row key mismatch corruption error.
933    pub(crate) fn persisted_row_key_mismatch() -> Self {
934        Self::store_corruption()
935    }
936
937    /// Construct the canonical persisted-row missing declared-field corruption error.
938    pub(crate) fn persisted_row_declared_field_missing(field_name: &str) -> Self {
939        Self::persisted_row_field_decode_corruption(field_name)
940    }
941
942    /// Construct the canonical reverse-index ordinal overflow internal error.
943    pub(crate) fn reverse_index_ordinal_overflow(
944        _source_path: &str,
945        _field_name: &str,
946        _target_path: &str,
947        _detail: impl Sized,
948    ) -> Self {
949        Self::index_internal()
950    }
951
952    /// Construct the canonical reverse-index entry corruption error.
953    pub(crate) fn reverse_index_entry_corrupted(
954        _source_path: &str,
955        _field_name: &str,
956        _target_path: &str,
957        _index_key: impl fmt::Debug,
958        _detail: impl Sized,
959    ) -> Self {
960        Self::index_corruption()
961    }
962
963    /// Construct the canonical relation-target store missing internal error.
964    pub(crate) fn relation_target_store_missing(
965        _source_path: &str,
966        _field_name: &str,
967        _target_path: &str,
968        _store_path: &str,
969        _detail: impl Sized,
970    ) -> Self {
971        Self::executor_internal()
972    }
973
974    /// Construct the canonical relation-target key decode corruption error.
975    pub(crate) fn relation_target_key_decode_failed(
976        _context_label: &str,
977        _source_path: &str,
978        _field_name: &str,
979        _target_path: &str,
980        _detail: impl Sized,
981    ) -> Self {
982        Self::identity_corruption()
983    }
984
985    /// Construct the canonical relation-target entity mismatch corruption error.
986    pub(crate) fn relation_target_entity_mismatch(
987        _context_label: &str,
988        _source_path: &str,
989        _field_name: &str,
990        _target_path: &str,
991        _target_entity_name: &str,
992        _expected_tag: impl Sized,
993        _actual_tag: impl Sized,
994    ) -> Self {
995        Self::store_corruption()
996    }
997
998    /// Construct the canonical relation-source row decode corruption error.
999    pub(crate) fn relation_source_row_decode_failed(
1000        _source_path: &str,
1001        _field_name: &str,
1002        _target_path: &str,
1003        _detail: impl Sized,
1004    ) -> Self {
1005        Self::persisted_row_decode_corruption()
1006    }
1007
1008    /// Construct the canonical relation-source unsupported scalar relation-key corruption error.
1009    pub(crate) fn relation_source_row_unsupported_scalar_relation_key(
1010        _source_path: &str,
1011        _field_name: &str,
1012        _target_path: &str,
1013    ) -> Self {
1014        Self::persisted_row_decode_corruption()
1015    }
1016
1017    /// Construct the canonical unsupported relation key-kind corruption error.
1018    pub(crate) fn relation_source_row_unsupported_key_kind(_field_kind: impl fmt::Debug) -> Self {
1019        Self::persisted_row_decode_corruption()
1020    }
1021
1022    /// Construct the canonical covering-component empty-payload corruption error.
1023    #[cfg(any(test, feature = "query"))]
1024    pub(crate) fn bytes_covering_component_payload_empty() -> Self {
1025        Self::index_corruption()
1026    }
1027
1028    /// Construct the canonical covering-component truncated bool corruption error.
1029    #[cfg(any(test, feature = "query"))]
1030    pub(crate) fn bytes_covering_bool_payload_truncated() -> Self {
1031        Self::index_corruption()
1032    }
1033
1034    /// Construct the canonical covering-component invalid-length corruption error.
1035    #[cfg(any(test, feature = "query"))]
1036    pub(crate) fn bytes_covering_component_payload_invalid_length() -> Self {
1037        Self::index_corruption()
1038    }
1039
1040    /// Construct the canonical covering-component invalid-bool corruption error.
1041    #[cfg(any(test, feature = "query"))]
1042    pub(crate) fn bytes_covering_bool_payload_invalid_value() -> Self {
1043        Self::index_corruption()
1044    }
1045
1046    /// Construct the canonical covering-component invalid text terminator corruption error.
1047    #[cfg(any(test, feature = "query"))]
1048    pub(crate) fn bytes_covering_text_payload_invalid_terminator() -> Self {
1049        Self::index_corruption()
1050    }
1051
1052    /// Construct the canonical covering-component trailing-text corruption error.
1053    #[cfg(any(test, feature = "query"))]
1054    pub(crate) fn bytes_covering_text_payload_trailing_bytes() -> Self {
1055        Self::index_corruption()
1056    }
1057
1058    /// Construct the canonical covering-component invalid-UTF-8 text corruption error.
1059    #[cfg(any(test, feature = "query"))]
1060    pub(crate) fn bytes_covering_text_payload_invalid_utf8() -> Self {
1061        Self::index_corruption()
1062    }
1063
1064    /// Construct the canonical covering-component invalid text escape corruption error.
1065    #[cfg(any(test, feature = "query"))]
1066    pub(crate) fn bytes_covering_text_payload_invalid_escape_byte() -> Self {
1067        Self::index_corruption()
1068    }
1069
1070    /// Construct the canonical covering-component missing text terminator corruption error.
1071    #[cfg(any(test, feature = "query"))]
1072    pub(crate) fn bytes_covering_text_payload_missing_terminator() -> Self {
1073        Self::index_corruption()
1074    }
1075
1076    /// Construct the canonical missing persisted-field decode error.
1077    #[must_use]
1078    pub fn missing_persisted_slot(field_name: &'static str) -> Self {
1079        Self::persisted_row_field_decode_corruption(field_name)
1080    }
1081
1082    /// Construct an identity-origin corruption error.
1083    pub(crate) fn identity_corruption() -> Self {
1084        Self::new(ErrorClass::Corruption, ErrorOrigin::Identity)
1085    }
1086
1087    /// Construct a store-origin unsupported error.
1088    #[cold]
1089    #[inline(never)]
1090    pub(crate) fn store_unsupported() -> Self {
1091        Self::new(ErrorClass::Unsupported, ErrorOrigin::Store)
1092    }
1093
1094    /// Construct the typed optimistic/idempotency conflict for schema application.
1095    pub(crate) fn schema_application_conflict() -> Self {
1096        Self::new(ErrorClass::Conflict, ErrorOrigin::Store)
1097    }
1098
1099    /// Construct the canonical schema DDL publication race error.
1100    #[cfg(any(test, feature = "query"))]
1101    pub(crate) fn schema_ddl_publication_race_lost(_entity_path: &str) -> Self {
1102        Self {
1103            class: ErrorClass::Unsupported,
1104            origin: ErrorOrigin::Store,
1105            detail: Some(ErrorDetail::Store(StoreError::SchemaDdlPublicationRaceLost)),
1106        }
1107    }
1108
1109    /// Construct the canonical current physical-rewrite migration rejection.
1110    #[cfg(feature = "sql")]
1111    pub(crate) fn schema_ddl_rewrite_requires_migration(_entity_path: &str) -> Self {
1112        Self {
1113            class: ErrorClass::Unsupported,
1114            origin: ErrorOrigin::Store,
1115            detail: Some(ErrorDetail::Store(
1116                StoreError::SchemaDdlRewriteRequiresMigration,
1117            )),
1118        }
1119    }
1120
1121    /// Construct the fail-closed journal mutation-revision exhaustion error.
1122    pub(crate) fn journal_mutation_revision_exhausted() -> Self {
1123        Self {
1124            class: ErrorClass::Unsupported,
1125            origin: ErrorOrigin::Store,
1126            detail: Some(ErrorDetail::Store(
1127                StoreError::JournalMutationRevisionExhausted,
1128            )),
1129        }
1130    }
1131
1132    /// Construct a bounded schema-transition resource rejection.
1133    pub(crate) fn schema_transition_budget_exceeded(
1134        resource: SchemaTransitionBudgetResource,
1135    ) -> Self {
1136        Self {
1137            class: ErrorClass::Unsupported,
1138            origin: ErrorOrigin::Store,
1139            detail: Some(ErrorDetail::Store(
1140                StoreError::SchemaTransitionBudgetExceeded { resource },
1141            )),
1142        }
1143    }
1144
1145    /// Construct the canonical unsupported persisted entity-tag store error.
1146    pub(crate) fn unsupported_entity_tag_in_data_store(
1147        _entity_tag: crate::types::EntityTag,
1148    ) -> Self {
1149        Self::store_unsupported()
1150    }
1151
1152    /// Construct the canonical commit-memory id registration failure.
1153    #[cfg(not(test))]
1154    pub(crate) fn commit_memory_id_registration_failed(_err: impl Sized) -> Self {
1155        Self::store_internal()
1156    }
1157
1158    /// Construct an index-origin unsupported error.
1159    pub(crate) fn index_unsupported() -> Self {
1160        Self::new(ErrorClass::Unsupported, ErrorOrigin::Index)
1161    }
1162
1163    /// Construct the canonical index-key component size-limit unsupported error.
1164    pub(crate) fn index_component_exceeds_max_size() -> Self {
1165        Self::index_unsupported()
1166    }
1167
1168    /// Construct a serialize-origin unsupported error.
1169    pub(crate) fn serialize_unsupported() -> Self {
1170        Self::new(ErrorClass::Unsupported, ErrorOrigin::Serialize)
1171    }
1172
1173    /// Construct a cursor-origin invalid-continuation error.
1174    #[cfg(any(test, feature = "query"))]
1175    pub(crate) fn cursor_invalid_continuation() -> Self {
1176        Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor)
1177    }
1178
1179    /// Construct a serialize-origin incompatible persisted-format error.
1180    pub(crate) fn serialize_incompatible_persisted_format() -> Self {
1181        Self::new(
1182            ErrorClass::IncompatiblePersistedFormat,
1183            ErrorOrigin::Serialize,
1184        )
1185    }
1186
1187    /// Construct a query-origin unsupported error preserving one SQL parser
1188    /// unsupported-feature code in structured error detail.
1189    #[cfg(feature = "sql")]
1190    pub(crate) fn query_unsupported_sql_feature(feature: diagnostic_code::SqlFeatureCode) -> Self {
1191        Self {
1192            class: ErrorClass::Unsupported,
1193            origin: ErrorOrigin::Query,
1194            detail: Some(ErrorDetail::Query(
1195                QueryErrorDetail::UnsupportedSqlFeature { feature },
1196            )),
1197        }
1198    }
1199
1200    /// Construct a query-origin unsupported SQL lowering error preserving one
1201    /// compact lowering reason in structured error detail.
1202    #[cfg(feature = "sql")]
1203    pub(crate) fn query_sql_lowering(reason: diagnostic_code::SqlLoweringCode) -> Self {
1204        Self {
1205            class: ErrorClass::Unsupported,
1206            origin: ErrorOrigin::Query,
1207            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlLowering { reason })),
1208        }
1209    }
1210
1211    /// Construct a query-origin unsupported projection error preserving one
1212    /// compact projection reason in structured error detail.
1213    #[cfg(any(test, feature = "query"))]
1214    pub(crate) fn query_unsupported_projection(
1215        reason: diagnostic_code::QueryProjectionCode,
1216    ) -> Self {
1217        Self {
1218            class: ErrorClass::Unsupported,
1219            origin: ErrorOrigin::Query,
1220            detail: Some(ErrorDetail::Query(
1221                QueryErrorDetail::UnsupportedProjection { reason },
1222            )),
1223        }
1224    }
1225
1226    /// Construct a query-origin unsupported aggregate target-field error.
1227    #[cfg(any(test, feature = "query"))]
1228    pub(crate) fn query_unknown_aggregate_target_field() -> Self {
1229        Self {
1230            class: ErrorClass::Unsupported,
1231            origin: ErrorOrigin::Query,
1232            detail: Some(ErrorDetail::Query(
1233                QueryErrorDetail::UnknownAggregateTargetField,
1234            )),
1235        }
1236    }
1237
1238    /// Construct a query-origin unsupported error preserving one SQL endpoint
1239    /// surface mismatch in structured error detail.
1240    #[cfg(feature = "sql")]
1241    pub(crate) fn query_sql_surface_mismatch(
1242        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1243    ) -> Self {
1244        Self {
1245            class: ErrorClass::Unsupported,
1246            origin: ErrorOrigin::Query,
1247            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlSurfaceMismatch {
1248                mismatch,
1249            })),
1250        }
1251    }
1252
1253    /// Construct a query-origin unsupported SQL write boundary error.
1254    pub(crate) fn query_sql_write_boundary(
1255        boundary: diagnostic_code::SqlWriteBoundaryCode,
1256    ) -> Self {
1257        Self {
1258            class: ErrorClass::Unsupported,
1259            origin: ErrorOrigin::Query,
1260            detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlWriteBoundary {
1261                boundary,
1262            })),
1263        }
1264    }
1265
1266    pub fn store_not_found(_key: impl Sized) -> Self {
1267        Self {
1268            class: ErrorClass::NotFound,
1269            origin: ErrorOrigin::Store,
1270            detail: Some(ErrorDetail::Store(StoreError::NotFound)),
1271        }
1272    }
1273
1274    /// Construct a standardized unsupported-entity-path error.
1275    pub fn unsupported_entity_path(_path: impl Sized) -> Self {
1276        Self::store_unsupported()
1277    }
1278
1279    #[must_use]
1280    pub const fn is_not_found(&self) -> bool {
1281        matches!(self.detail, Some(ErrorDetail::Store(StoreError::NotFound)))
1282    }
1283
1284    /// Construct an index-plan corruption error with a canonical prefix.
1285    #[cold]
1286    #[inline(never)]
1287    pub(crate) fn index_plan_corruption(origin: ErrorOrigin) -> Self {
1288        Self::new(ErrorClass::Corruption, origin)
1289    }
1290
1291    /// Construct an index-plan corruption error for index-origin failures.
1292    #[cold]
1293    #[inline(never)]
1294    pub(crate) fn index_plan_index_corruption() -> Self {
1295        Self::index_plan_corruption(ErrorOrigin::Index)
1296    }
1297
1298    /// Construct an index-plan corruption error for store-origin failures.
1299    #[cold]
1300    #[inline(never)]
1301    pub(crate) fn index_plan_store_corruption() -> Self {
1302        Self::index_plan_corruption(ErrorOrigin::Store)
1303    }
1304
1305    /// Construct an index-plan corruption error for serialize-origin failures.
1306    #[cold]
1307    #[inline(never)]
1308    pub(crate) fn index_plan_serialize_corruption() -> Self {
1309        Self::index_plan_corruption(ErrorOrigin::Serialize)
1310    }
1311
1312    /// Construct an index-plan invariant violation error with a canonical prefix.
1313    #[cfg(test)]
1314    pub(crate) fn index_plan_invariant(origin: ErrorOrigin) -> Self {
1315        Self::new(ErrorClass::InvariantViolation, origin)
1316    }
1317
1318    /// Construct an index-plan invariant violation error for store-origin failures.
1319    #[cfg(test)]
1320    pub(crate) fn index_plan_store_invariant() -> Self {
1321        Self::index_plan_invariant(ErrorOrigin::Store)
1322    }
1323
1324    /// Construct an index-origin conflict without claiming accepted identity.
1325    ///
1326    /// Live accepted uniqueness violations use `ConstraintDiagnostic`.
1327    /// Schema-domain staging and activation findings use this compact
1328    /// classification before an accepted write-admission diagnostic exists.
1329    pub(crate) fn index_conflict() -> Self {
1330        Self::new(ErrorClass::Conflict, ErrorOrigin::Index)
1331    }
1332}
1333
1334impl From<diagnostic_code::QueryReadAdmissionCode> for InternalError {
1335    fn from(reason: diagnostic_code::QueryReadAdmissionCode) -> Self {
1336        Self {
1337            class: ErrorClass::Unsupported,
1338            origin: ErrorOrigin::Query,
1339            detail: Some(ErrorDetail::Query(QueryErrorDetail::QueryReadAdmission {
1340                reason,
1341            })),
1342        }
1343    }
1344}
1345
1346impl fmt::Debug for InternalError {
1347    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1348        fmt_compact_diagnostic(
1349            f,
1350            self.diagnostic_code(),
1351            self.detail
1352                .as_ref()
1353                .and_then(ErrorDetail::diagnostic_detail),
1354        )
1355    }
1356}
1357
1358impl fmt::Display for InternalError {
1359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1360        f.write_str(self.message())
1361    }
1362}
1363
1364impl std::error::Error for InternalError {}
1365
1366///
1367/// ConstraintDiagnosticKind
1368///
1369/// Accepted constraint family attached to one bounded runtime diagnostic.
1370/// Accepted schema owns the identity; this enum is its error-boundary projection.
1371///
1372
1373#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
1374pub enum ConstraintDiagnosticKind {
1375    /// One accepted canonical check expression.
1376    Check,
1377
1378    /// One accepted or activating not-null field contract.
1379    NotNull,
1380
1381    /// One accepted or activating relation contract.
1382    Relation,
1383
1384    /// One accepted durable rule over a nominal value below a persisted root.
1385    TargetedRule,
1386
1387    /// One accepted or activating unique-index contract.
1388    Unique,
1389}
1390
1391impl ConstraintDiagnosticKind {
1392    /// Borrow the stable public label for this constraint family.
1393    #[must_use]
1394    pub const fn as_str(self) -> &'static str {
1395        match self {
1396            Self::Check => "check",
1397            Self::NotNull => "not_null",
1398            Self::Relation => "relation",
1399            Self::TargetedRule => "targeted_rule",
1400            Self::Unique => "unique",
1401        }
1402    }
1403}
1404
1405///
1406/// ConstraintValuePathComponent
1407///
1408/// Stable accepted identity or finite-value coordinate in one targeted-rule
1409/// violation. Display names are deliberately absent so renames cannot change
1410/// the diagnostic identity.
1411///
1412
1413#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
1414pub enum ConstraintValuePathComponent {
1415    /// Persisted root field whose admitted value was traversed.
1416    RootField { field_id: u32 },
1417
1418    /// Accepted record member selected by immutable composite/member identity.
1419    RecordMember {
1420        composite_type_id: u32,
1421        member_id: u32,
1422    },
1423
1424    /// Tuple element selected by accepted composite identity and ordinal.
1425    TupleElement {
1426        composite_type_id: u32,
1427        ordinal: u32,
1428    },
1429
1430    /// Transparent accepted newtype boundary.
1431    Newtype { composite_type_id: u32 },
1432
1433    /// Selected accepted enum variant.
1434    EnumVariant { enum_type_id: u32, variant_id: u32 },
1435
1436    /// List element in admitted order.
1437    ListElement { index: u32 },
1438
1439    /// Set element in canonical admitted order.
1440    SetElement { index: u32 },
1441
1442    /// Map key in canonical entry order.
1443    MapEntryKey { index: u32 },
1444
1445    /// Map value in canonical entry order.
1446    MapEntryValue { index: u32 },
1447}
1448
1449impl fmt::Display for ConstraintValuePathComponent {
1450    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1451        match self {
1452            Self::RootField { field_id } => write!(f, "field#{field_id}"),
1453            Self::RecordMember {
1454                composite_type_id,
1455                member_id,
1456            } => write!(f, "record#{composite_type_id}.member#{member_id}"),
1457            Self::TupleElement {
1458                composite_type_id,
1459                ordinal,
1460            } => write!(f, "tuple#{composite_type_id}[{ordinal}]"),
1461            Self::Newtype { composite_type_id } => write!(f, "newtype#{composite_type_id}"),
1462            Self::EnumVariant {
1463                enum_type_id,
1464                variant_id,
1465            } => write!(f, "enum#{enum_type_id}.variant#{variant_id}"),
1466            Self::ListElement { index } => write!(f, "list[{index}]"),
1467            Self::SetElement { index } => write!(f, "set[{index}]"),
1468            Self::MapEntryKey { index } => write!(f, "map[{index}].key"),
1469            Self::MapEntryValue { index } => write!(f, "map[{index}].value"),
1470        }
1471    }
1472}
1473
1474///
1475/// ConstraintValuePath
1476///
1477/// Bounded typed path to the first deterministic failing value occurrence.
1478///
1479
1480#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
1481pub struct ConstraintValuePath {
1482    components: Vec<ConstraintValuePathComponent>,
1483}
1484
1485impl ConstraintValuePath {
1486    /// Build one already-bounded accepted occurrence path.
1487    #[must_use]
1488    pub(crate) const fn new(components: Vec<ConstraintValuePathComponent>) -> Self {
1489        Self { components }
1490    }
1491
1492    /// Borrow the stable accepted components.
1493    #[must_use]
1494    pub const fn components(&self) -> &[ConstraintValuePathComponent] {
1495        self.components.as_slice()
1496    }
1497}
1498
1499impl fmt::Display for ConstraintValuePath {
1500    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1501        for (ordinal, component) in self.components.iter().enumerate() {
1502            if ordinal != 0 {
1503                f.write_str("/")?;
1504            }
1505            component.fmt(f)?;
1506        }
1507        Ok(())
1508    }
1509}
1510
1511///
1512/// ConstraintDiagnosticContext
1513///
1514/// Boundary at which one accepted constraint failure was observed.
1515/// This distinguishes incoming write rejection from historical validation.
1516///
1517
1518#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
1519pub enum ConstraintDiagnosticContext {
1520    /// Integrity verification found invalid already-accepted state.
1521    Integrity,
1522
1523    /// Bounded activation validation found an incompatible historical row.
1524    MigrationValidation,
1525
1526    /// A new mutation after-image violated accepted admission authority.
1527    WriteAdmission,
1528}
1529
1530impl ConstraintDiagnosticContext {
1531    /// Borrow the stable public label for this diagnostic context.
1532    #[must_use]
1533    pub const fn as_str(self) -> &'static str {
1534        match self {
1535            Self::Integrity => "integrity",
1536            Self::MigrationValidation => "migration_validation",
1537            Self::WriteAdmission => "write_admission",
1538        }
1539    }
1540}
1541
1542///
1543/// ConstraintDiagnostic
1544///
1545/// One bounded accepted-constraint failure carried through runtime and SQL
1546/// boundaries. It is a projection of accepted identity, not a second schema
1547/// authority, and its compact error code remains the classification owner.
1548///
1549
1550#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
1551pub struct ConstraintDiagnostic {
1552    constraint_id: u32,
1553    constraint_name: String,
1554    constraint_kind: ConstraintDiagnosticKind,
1555    entity: String,
1556    primary_key: Option<Vec<u8>>,
1557    field_paths: Vec<String>,
1558    value_path: Option<Box<ConstraintValuePath>>,
1559    context: ConstraintDiagnosticContext,
1560    error_code: u16,
1561}
1562
1563impl ConstraintDiagnostic {
1564    /// Build one incoming-write accepted constraint violation.
1565    #[must_use]
1566    pub(crate) const fn write_violation(
1567        constraint_id: u32,
1568        constraint_name: String,
1569        constraint_kind: ConstraintDiagnosticKind,
1570        entity: String,
1571        primary_key: Option<Vec<u8>>,
1572        field_paths: Vec<String>,
1573    ) -> Self {
1574        Self {
1575            constraint_id,
1576            constraint_name,
1577            constraint_kind,
1578            entity,
1579            primary_key,
1580            field_paths,
1581            value_path: None,
1582            context: ConstraintDiagnosticContext::WriteAdmission,
1583            error_code: diagnostic_code::ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION.raw(),
1584        }
1585    }
1586
1587    /// Build one incoming-write targeted-rule violation.
1588    #[must_use]
1589    pub(crate) fn write_targeted_rule_violation(
1590        constraint_id: u32,
1591        constraint_name: String,
1592        entity: String,
1593        primary_key: Option<Vec<u8>>,
1594        field_paths: Vec<String>,
1595        value_path: ConstraintValuePath,
1596    ) -> Self {
1597        Self {
1598            constraint_id,
1599            constraint_name,
1600            constraint_kind: ConstraintDiagnosticKind::TargetedRule,
1601            entity,
1602            primary_key,
1603            field_paths,
1604            value_path: Some(Box::new(value_path)),
1605            context: ConstraintDiagnosticContext::WriteAdmission,
1606            error_code: diagnostic_code::ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION.raw(),
1607        }
1608    }
1609
1610    /// Build one incoming-write activation barrier rejection.
1611    #[must_use]
1612    pub(crate) const fn write_activation_blocked(
1613        constraint_id: u32,
1614        constraint_name: String,
1615        constraint_kind: ConstraintDiagnosticKind,
1616        entity: String,
1617        primary_key: Option<Vec<u8>>,
1618        field_paths: Vec<String>,
1619    ) -> Self {
1620        Self {
1621            constraint_id,
1622            constraint_name,
1623            constraint_kind,
1624            entity,
1625            primary_key,
1626            field_paths,
1627            value_path: None,
1628            context: ConstraintDiagnosticContext::WriteAdmission,
1629            error_code:
1630                diagnostic_code::ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_ACTIVATION_WRITE_BLOCKED
1631                    .raw(),
1632        }
1633    }
1634
1635    /// Build one historical activation finding from its durable compact code.
1636    #[must_use]
1637    pub(crate) const fn migration_validation(
1638        constraint_id: u32,
1639        constraint_name: String,
1640        constraint_kind: ConstraintDiagnosticKind,
1641        entity: String,
1642        primary_key: Vec<u8>,
1643        field_paths: Vec<String>,
1644        error_code: u16,
1645    ) -> Self {
1646        Self {
1647            constraint_id,
1648            constraint_name,
1649            constraint_kind,
1650            entity,
1651            primary_key: Some(primary_key),
1652            field_paths,
1653            value_path: None,
1654            context: ConstraintDiagnosticContext::MigrationValidation,
1655            error_code,
1656        }
1657    }
1658
1659    /// Build one historical targeted-rule finding with durable path evidence.
1660    #[must_use]
1661    pub(crate) fn migration_targeted_rule_validation(
1662        constraint_id: u32,
1663        constraint_name: String,
1664        entity: String,
1665        primary_key: Vec<u8>,
1666        field_paths: Vec<String>,
1667        value_path: ConstraintValuePath,
1668        error_code: u16,
1669    ) -> Self {
1670        Self {
1671            constraint_id,
1672            constraint_name,
1673            constraint_kind: ConstraintDiagnosticKind::TargetedRule,
1674            entity,
1675            primary_key: Some(primary_key),
1676            field_paths,
1677            value_path: Some(Box::new(value_path)),
1678            context: ConstraintDiagnosticContext::MigrationValidation,
1679            error_code,
1680        }
1681    }
1682
1683    /// Return the stable accepted constraint identity.
1684    #[must_use]
1685    pub const fn constraint_id(&self) -> u32 {
1686        self.constraint_id
1687    }
1688
1689    /// Borrow the stable accepted constraint name.
1690    #[must_use]
1691    pub const fn constraint_name(&self) -> &str {
1692        self.constraint_name.as_str()
1693    }
1694
1695    /// Return the accepted constraint family.
1696    #[must_use]
1697    pub const fn constraint_kind(&self) -> ConstraintDiagnosticKind {
1698        self.constraint_kind
1699    }
1700
1701    /// Borrow the accepted entity identity.
1702    #[must_use]
1703    pub const fn entity(&self) -> &str {
1704        self.entity.as_str()
1705    }
1706
1707    /// Borrow the canonical persisted primary-key bytes when available.
1708    #[must_use]
1709    pub fn primary_key(&self) -> Option<&[u8]> {
1710        self.primary_key.as_deref()
1711    }
1712
1713    /// Borrow bounded accepted field paths implicated by the failure.
1714    #[must_use]
1715    pub const fn field_paths(&self) -> &[String] {
1716        self.field_paths.as_slice()
1717    }
1718
1719    /// Borrow the typed concrete value path for a targeted-rule violation.
1720    #[must_use]
1721    pub fn value_path(&self) -> Option<&ConstraintValuePath> {
1722        self.value_path.as_deref()
1723    }
1724
1725    /// Return the boundary that observed the failure.
1726    #[must_use]
1727    pub const fn context(&self) -> ConstraintDiagnosticContext {
1728        self.context
1729    }
1730
1731    /// Return the compact stable error code for this exact failure.
1732    #[must_use]
1733    pub const fn error_code(&self) -> diagnostic_code::ErrorCode {
1734        diagnostic_code::ErrorCode::from_raw(self.error_code)
1735    }
1736
1737    /// Return the broad public error class derived from the compact code.
1738    #[must_use]
1739    pub const fn error_class(&self) -> diagnostic_code::ErrorClass {
1740        self.error_code().class()
1741    }
1742}
1743
1744///
1745/// ErrorDetail
1746///
1747/// Structured, origin-specific error detail carried by [`InternalError`].
1748/// This enum is intentionally extensible.
1749///
1750
1751pub enum ErrorDetail {
1752    /// Executor-owned mutation and query execution details.
1753    Executor(ExecutorErrorDetail),
1754    Store(StoreError),
1755    Query(QueryErrorDetail),
1756    Recovery(RecoveryErrorDetail),
1757    /// Persisted-row serialization and decoding details.
1758    Serialize(SerializeErrorDetail),
1759    // Future-proofing:
1760    // Index(IndexError),
1761}
1762
1763/// Executor-specific structured error detail.
1764pub enum ExecutorErrorDetail {
1765    /// A complete insert or replacement omitted one or more required fields.
1766    MutationRequiredFieldMissing,
1767    /// A logical mutation would move accepted managed time backward.
1768    MutationManagedTimestampRegression,
1769    /// A caller explicitly authored a field owned by accepted database policy.
1770    MutationDatabaseOwnedFieldExplicit,
1771    /// A final canonical after-image violated one accepted constraint or activation gate.
1772    ConstraintViolation {
1773        diagnostic: Box<ConstraintDiagnostic>,
1774    },
1775    /// Accepted row-constraint metadata or compiled state was inconsistent.
1776    AcceptedRowConstraintProgramCorrupt,
1777    /// A write would rely on one incomplete activation-owned physical proof.
1778    ConstraintActivationWriteBlocked {
1779        diagnostic: Box<ConstraintDiagnostic>,
1780    },
1781}
1782
1783impl ExecutorErrorDetail {
1784    /// Borrow the accepted constraint diagnostic carried by this failure.
1785    #[must_use]
1786    pub fn constraint_diagnostic(&self) -> Option<&ConstraintDiagnostic> {
1787        match self {
1788            Self::ConstraintActivationWriteBlocked { diagnostic }
1789            | Self::ConstraintViolation { diagnostic } => Some(diagnostic.as_ref()),
1790            Self::MutationRequiredFieldMissing
1791            | Self::MutationManagedTimestampRegression
1792            | Self::MutationDatabaseOwnedFieldExplicit
1793            | Self::AcceptedRowConstraintProgramCorrupt => None,
1794        }
1795    }
1796}
1797
1798/// Persisted-row serialization and decoding error detail.
1799pub enum SerializeErrorDetail {
1800    /// The row stamp is older or newer than the accepted layout window.
1801    PersistedRowLayoutOutsideAcceptedWindow,
1802
1803    /// The physical slot count does not match the row's stamped layout.
1804    PersistedRowSlotCountMismatch,
1805}
1806
1807///
1808/// RecoveryErrorDetail
1809///
1810/// Recovery-origin structured error detail payload.
1811///
1812
1813pub enum RecoveryErrorDetail {
1814    UnsupportedFormatVersion { found: Option<u16>, required: u16 },
1815
1816    MalformedFormatMarker { reason: RecoveryFormatMarkerError },
1817}
1818
1819/// Store boot-marker corruption classification.
1820#[derive(Clone, Copy, Eq, PartialEq)]
1821pub enum RecoveryFormatMarkerError {
1822    Magic,
1823    Checksum,
1824    State,
1825}
1826
1827///
1828/// StoreError
1829///
1830/// Store-specific structured error detail.
1831/// Never returned directly; always wrapped in [`ErrorDetail::Store`].
1832///
1833
1834pub enum StoreError {
1835    NotFound,
1836
1837    Corrupt,
1838
1839    InvariantViolation,
1840
1841    SchemaDdlPublicationRaceLost,
1842
1843    SchemaDdlRewriteRequiresMigration,
1844
1845    SchemaRowLayoutVersionExhausted,
1846
1847    JournalMutationRevisionExhausted,
1848
1849    SchemaTransitionBudgetExceeded {
1850        resource: SchemaTransitionBudgetResource,
1851    },
1852
1853    /// A generated field would collide with an accepted DDL-owned slot.
1854    SchemaGeneratedFieldAfterDdlField,
1855
1856    /// A live generated constraint activation no longer matches its proposal.
1857    SchemaGeneratedConstraintActivationStale,
1858}
1859
1860///
1861/// QueryErrorDetail
1862///
1863/// Query-origin structured error detail payload.
1864///
1865
1866pub enum QueryErrorDetail {
1867    NumericOverflow,
1868
1869    NumericNotRepresentable,
1870
1871    UnsupportedSqlFeature {
1872        feature: diagnostic_code::SqlFeatureCode,
1873    },
1874
1875    SqlLowering {
1876        reason: diagnostic_code::SqlLoweringCode,
1877    },
1878
1879    UnsupportedProjection {
1880        reason: diagnostic_code::QueryProjectionCode,
1881    },
1882
1883    UnknownAggregateTargetField,
1884
1885    ResultShapeMismatch {
1886        reason: diagnostic_code::QueryResultShapeCode,
1887    },
1888
1889    QueryReadAdmission {
1890        reason: diagnostic_code::QueryReadAdmissionCode,
1891    },
1892
1893    SqlSurfaceMismatch {
1894        mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1895    },
1896
1897    SqlWriteBoundary {
1898        boundary: diagnostic_code::SqlWriteBoundaryCode,
1899    },
1900
1901    SchemaDdlAdmission {
1902        error: SchemaDdlAdmissionError,
1903    },
1904
1905    StaleSchemaRevision,
1906}
1907
1908impl fmt::Display for QueryErrorDetail {
1909    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1910        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
1911    }
1912}
1913
1914impl std::error::Error for QueryErrorDetail {}
1915
1916///
1917/// SchemaTransitionBudgetResource
1918///
1919/// Query-visible identity of the exact schema-transition resource cap that
1920/// rejected a complete validation or derived-state stage.
1921///
1922
1923#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1924pub enum SchemaTransitionBudgetResource {
1925    /// Number of physical deletion keys retained for replacement.
1926    DeletionKeys,
1927    /// Number of row-derived projection entries retained for validation.
1928    ProjectionEntries,
1929    /// Deterministic projection and physical-classification work units.
1930    ProjectionWorkUnits,
1931    /// Number of authoritative source rows.
1932    SourceRows,
1933    /// Cumulative bytes of authoritative source rows.
1934    SourceRowBytes,
1935    /// Retained raw payloads plus deterministic-sort workspace bytes.
1936    StagedRawBytes,
1937}
1938
1939///
1940/// SchemaDdlAdmissionError
1941///
1942/// Stable query-visible SQL DDL admission reason. Human diagnostics may carry
1943/// extra version, fingerprint, and target facts beside this machine-readable
1944/// variant.
1945///
1946
1947#[derive(Clone, Copy, Eq, PartialEq)]
1948pub enum SchemaDdlAdmissionError {
1949    MissingExpectedSchemaVersion,
1950
1951    MissingNextSchemaVersion,
1952
1953    StaleExpectedSchemaVersion,
1954
1955    InvalidExpectedSchemaVersion,
1956
1957    InvalidNextSchemaVersion,
1958
1959    AcceptedSchemaChangeWithoutVersionBump,
1960
1961    EmptyVersionBump,
1962
1963    VersionGap,
1964
1965    VersionRollback,
1966
1967    FingerprintMethodMismatch,
1968
1969    UnsupportedTransitionClass,
1970
1971    PhysicalRunnerMissing,
1972
1973    ValidationFailed,
1974
1975    PublicationRaceLost,
1976
1977    InvalidAddColumnDefault,
1978
1979    InvalidAlterColumnDefault,
1980
1981    RowLayoutVersionExhausted,
1982
1983    GeneratedIndexDropRejected,
1984
1985    SchemaRewriteRequiresMigration,
1986
1987    SchemaTransitionBudgetExceeded {
1988        resource: SchemaTransitionBudgetResource,
1989    },
1990
1991    GeneratedFieldDefaultChangeRejected,
1992
1993    GeneratedFieldNullabilityChangeRejected,
1994}
1995
1996impl fmt::Display for SchemaDdlAdmissionError {
1997    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1998        f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
1999    }
2000}
2001
2002impl std::error::Error for SchemaDdlAdmissionError {}
2003
2004impl fmt::Debug for ErrorDetail {
2005    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2006        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2007    }
2008}
2009
2010impl fmt::Debug for ExecutorErrorDetail {
2011    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2012        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2013    }
2014}
2015
2016impl fmt::Debug for StoreError {
2017    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2018        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2019    }
2020}
2021
2022impl fmt::Debug for QueryErrorDetail {
2023    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2024        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2025    }
2026}
2027
2028impl fmt::Debug for RecoveryErrorDetail {
2029    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2030        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2031    }
2032}
2033
2034impl fmt::Debug for SerializeErrorDetail {
2035    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2036        fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
2037    }
2038}
2039
2040impl fmt::Debug for RecoveryFormatMarkerError {
2041    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2042        fmt_compact_diagnostic(
2043            f,
2044            diagnostic_code::DiagnosticCode::RuntimeCorruption,
2045            Some(diagnostic_code::DiagnosticDetail::RuntimeKind {
2046                kind: diagnostic_code::RuntimeErrorKind::Corruption,
2047            }),
2048        )
2049    }
2050}
2051
2052impl fmt::Debug for SchemaDdlAdmissionError {
2053    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2054        fmt_compact_diagnostic(
2055            f,
2056            diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
2057            Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2058                reason: self.diagnostic_code(),
2059            }),
2060        )
2061    }
2062}
2063
2064fn fmt_compact_diagnostic(
2065    f: &mut fmt::Formatter<'_>,
2066    code: diagnostic_code::DiagnosticCode,
2067    detail: Option<diagnostic_code::DiagnosticDetail>,
2068) -> fmt::Result {
2069    write!(
2070        f,
2071        "{}",
2072        diagnostic_code::ErrorCode::from_parts(code, detail).raw()
2073    )
2074}
2075
2076impl ErrorDetail {
2077    /// Return the compact diagnostic code for this structured detail.
2078    #[must_use]
2079    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2080        match self {
2081            Self::Executor(error) => error.diagnostic_code(),
2082            Self::Store(error) => error.diagnostic_code(),
2083            Self::Query(error) => error.diagnostic_code(),
2084            Self::Recovery(error) => error.diagnostic_code(),
2085            Self::Serialize(error) => error.diagnostic_code(),
2086        }
2087    }
2088
2089    /// Return compact structured diagnostic detail when the payload carries one.
2090    #[must_use]
2091    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2092        match self {
2093            Self::Executor(error) => error.diagnostic_detail(),
2094            Self::Store(error) => error.diagnostic_detail(),
2095            Self::Query(error) => error.diagnostic_detail(),
2096            Self::Recovery(error) => error.diagnostic_detail(),
2097            Self::Serialize(error) => error.diagnostic_detail(),
2098        }
2099    }
2100}
2101
2102impl ExecutorErrorDetail {
2103    /// Return the compact diagnostic code for this executor detail.
2104    #[must_use]
2105    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2106        match self {
2107            Self::MutationRequiredFieldMissing | Self::MutationDatabaseOwnedFieldExplicit => {
2108                diagnostic_code::DiagnosticCode::RuntimeUnsupported
2109            }
2110            Self::MutationManagedTimestampRegression => {
2111                diagnostic_code::DiagnosticCode::RuntimeInvariantViolation
2112            }
2113            Self::ConstraintViolation { diagnostic }
2114            | Self::ConstraintActivationWriteBlocked { diagnostic } => {
2115                diagnostic.error_code().diagnostic_code()
2116            }
2117            Self::AcceptedRowConstraintProgramCorrupt => {
2118                diagnostic_code::DiagnosticCode::RuntimeCorruption
2119            }
2120        }
2121    }
2122
2123    /// Return compact structured diagnostic detail for this executor detail.
2124    #[must_use]
2125    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2126        match self {
2127            Self::MutationRequiredFieldMissing => {
2128                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2129                    boundary: diagnostic_code::RuntimeBoundaryCode::MutationRequiredFieldMissing,
2130                })
2131            }
2132            Self::MutationDatabaseOwnedFieldExplicit => {
2133                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2134                    boundary:
2135                        diagnostic_code::RuntimeBoundaryCode::MutationDatabaseOwnedFieldExplicit,
2136                })
2137            }
2138            Self::MutationManagedTimestampRegression => {
2139                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2140                    boundary:
2141                        diagnostic_code::RuntimeBoundaryCode::MutationManagedTimestampRegression,
2142                })
2143            }
2144            Self::ConstraintViolation { diagnostic }
2145            | Self::ConstraintActivationWriteBlocked { diagnostic } => {
2146                diagnostic.error_code().diagnostic_detail()
2147            }
2148            Self::AcceptedRowConstraintProgramCorrupt => {
2149                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2150                    boundary:
2151                        diagnostic_code::RuntimeBoundaryCode::AcceptedRowConstraintProgramCorrupt,
2152                })
2153            }
2154        }
2155    }
2156}
2157
2158impl RecoveryErrorDetail {
2159    /// Return the compact diagnostic code for this recovery detail.
2160    #[must_use]
2161    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2162        match self {
2163            Self::UnsupportedFormatVersion { .. } => {
2164                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
2165            }
2166            Self::MalformedFormatMarker { .. } => {
2167                diagnostic_code::DiagnosticCode::RuntimeCorruption
2168            }
2169        }
2170    }
2171
2172    /// Return compact structured diagnostic detail for this recovery detail.
2173    #[must_use]
2174    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2175        let kind = match self {
2176            Self::UnsupportedFormatVersion { .. } => {
2177                diagnostic_code::RuntimeErrorKind::IncompatiblePersistedFormat
2178            }
2179            Self::MalformedFormatMarker { .. } => diagnostic_code::RuntimeErrorKind::Corruption,
2180        };
2181
2182        Some(diagnostic_code::DiagnosticDetail::RuntimeKind { kind })
2183    }
2184}
2185
2186impl SerializeErrorDetail {
2187    /// Return the compact diagnostic code for this serialization detail.
2188    #[must_use]
2189    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2190        match self {
2191            Self::PersistedRowLayoutOutsideAcceptedWindow | Self::PersistedRowSlotCountMismatch => {
2192                diagnostic_code::DiagnosticCode::RuntimeCorruption
2193            }
2194        }
2195    }
2196
2197    /// Return compact structured diagnostic detail for this serialization detail.
2198    #[must_use]
2199    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2200        let boundary = match self {
2201            Self::PersistedRowLayoutOutsideAcceptedWindow => {
2202                diagnostic_code::RuntimeBoundaryCode::PersistedRowLayoutOutsideAcceptedWindow
2203            }
2204            Self::PersistedRowSlotCountMismatch => {
2205                diagnostic_code::RuntimeBoundaryCode::PersistedRowSlotCountMismatch
2206            }
2207        };
2208
2209        Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary { boundary })
2210    }
2211}
2212
2213impl StoreError {
2214    /// Return the compact diagnostic code for this store detail.
2215    #[must_use]
2216    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2217        match self {
2218            Self::NotFound => diagnostic_code::DiagnosticCode::StoreNotFound,
2219            Self::Corrupt => diagnostic_code::DiagnosticCode::StoreCorruption,
2220            Self::InvariantViolation => diagnostic_code::DiagnosticCode::StoreInvariantViolation,
2221            Self::SchemaDdlPublicationRaceLost
2222            | Self::SchemaDdlRewriteRequiresMigration
2223            | Self::SchemaRowLayoutVersionExhausted
2224            | Self::SchemaTransitionBudgetExceeded { .. } => {
2225                diagnostic_code::DiagnosticCode::SchemaDdlAdmission
2226            }
2227            Self::JournalMutationRevisionExhausted | Self::SchemaGeneratedFieldAfterDdlField => {
2228                diagnostic_code::DiagnosticCode::RuntimeUnsupported
2229            }
2230            Self::SchemaGeneratedConstraintActivationStale => {
2231                diagnostic_code::DiagnosticCode::RuntimeConflict
2232            }
2233        }
2234    }
2235
2236    /// Return compact structured diagnostic detail when the store error has one.
2237    #[must_use]
2238    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2239        match self {
2240            Self::SchemaDdlPublicationRaceLost => {
2241                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2242                    reason: diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost,
2243                })
2244            }
2245            Self::SchemaDdlRewriteRequiresMigration => {
2246                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2247                    reason: diagnostic_code::SchemaDdlAdmissionCode::SchemaRewriteRequiresMigration,
2248                })
2249            }
2250            Self::SchemaRowLayoutVersionExhausted => {
2251                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2252                    reason: diagnostic_code::SchemaDdlAdmissionCode::RowLayoutVersionExhausted,
2253                })
2254            }
2255            Self::JournalMutationRevisionExhausted => {
2256                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2257                    boundary:
2258                        diagnostic_code::RuntimeBoundaryCode::JournalMutationRevisionExhausted,
2259                })
2260            }
2261            Self::SchemaTransitionBudgetExceeded { .. } => {
2262                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2263                    reason: diagnostic_code::SchemaDdlAdmissionCode::SchemaTransitionBudgetExceeded,
2264                })
2265            }
2266            Self::SchemaGeneratedFieldAfterDdlField => {
2267                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2268                    boundary: diagnostic_code::RuntimeBoundaryCode::GeneratedFieldAfterDdlField,
2269                })
2270            }
2271            Self::SchemaGeneratedConstraintActivationStale => {
2272                Some(diagnostic_code::DiagnosticDetail::RuntimeBoundary {
2273                    boundary:
2274                        diagnostic_code::RuntimeBoundaryCode::GeneratedConstraintActivationStale,
2275                })
2276            }
2277            Self::NotFound | Self::Corrupt | Self::InvariantViolation => None,
2278        }
2279    }
2280}
2281
2282impl QueryErrorDetail {
2283    /// Return the compact diagnostic code for this query detail.
2284    #[must_use]
2285    pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
2286        match self {
2287            Self::NumericOverflow => diagnostic_code::DiagnosticCode::QueryNumericOverflow,
2288            Self::NumericNotRepresentable => {
2289                diagnostic_code::DiagnosticCode::QueryNumericNotRepresentable
2290            }
2291            Self::UnsupportedSqlFeature { .. } => {
2292                diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature
2293            }
2294            Self::SqlLowering { .. } => diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature,
2295            Self::UnsupportedProjection { .. } => {
2296                diagnostic_code::DiagnosticCode::QueryUnsupportedProjection
2297            }
2298            Self::UnknownAggregateTargetField => {
2299                diagnostic_code::DiagnosticCode::QueryUnknownAggregateTargetField
2300            }
2301            Self::ResultShapeMismatch { .. } => {
2302                diagnostic_code::DiagnosticCode::QueryResultShapeMismatch
2303            }
2304            Self::QueryReadAdmission { .. } => diagnostic_code::DiagnosticCode::QueryReadAdmission,
2305            Self::SqlSurfaceMismatch { .. } => {
2306                diagnostic_code::DiagnosticCode::QuerySqlSurfaceMismatch
2307            }
2308            Self::SqlWriteBoundary { .. } => diagnostic_code::DiagnosticCode::QuerySqlWriteBoundary,
2309            Self::SchemaDdlAdmission { .. } => diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
2310            Self::StaleSchemaRevision => diagnostic_code::DiagnosticCode::RuntimeConflict,
2311        }
2312    }
2313
2314    /// Return compact structured diagnostic detail when the query detail has one.
2315    #[must_use]
2316    pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
2317        match self {
2318            Self::UnsupportedSqlFeature { feature } => {
2319                Some(diagnostic_code::DiagnosticDetail::UnsupportedSqlFeature { feature: *feature })
2320            }
2321            Self::SqlLowering { reason } => {
2322                Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason: *reason })
2323            }
2324            Self::UnsupportedProjection { reason } => {
2325                Some(diagnostic_code::DiagnosticDetail::QueryProjection { reason: *reason })
2326            }
2327            Self::ResultShapeMismatch { reason } => {
2328                Some(diagnostic_code::DiagnosticDetail::QueryResultShape { reason: *reason })
2329            }
2330            Self::QueryReadAdmission { reason } => {
2331                Some(diagnostic_code::DiagnosticDetail::QueryReadAdmission { reason: *reason })
2332            }
2333            Self::SqlSurfaceMismatch { mismatch } => {
2334                Some(diagnostic_code::DiagnosticDetail::SqlSurfaceMismatch {
2335                    mismatch: *mismatch,
2336                })
2337            }
2338            Self::SqlWriteBoundary { boundary } => {
2339                Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary {
2340                    boundary: *boundary,
2341                })
2342            }
2343            Self::SchemaDdlAdmission { error } => {
2344                Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
2345                    reason: error.diagnostic_code(),
2346                })
2347            }
2348            Self::NumericOverflow
2349            | Self::NumericNotRepresentable
2350            | Self::UnknownAggregateTargetField
2351            | Self::StaleSchemaRevision => None,
2352        }
2353    }
2354}
2355
2356impl SchemaDdlAdmissionError {
2357    /// Return the compact diagnostic code for this SQL DDL admission reason.
2358    #[must_use]
2359    pub const fn diagnostic_code(&self) -> diagnostic_code::SchemaDdlAdmissionCode {
2360        match self {
2361            Self::MissingExpectedSchemaVersion => {
2362                diagnostic_code::SchemaDdlAdmissionCode::MissingExpectedSchemaVersion
2363            }
2364            Self::MissingNextSchemaVersion => {
2365                diagnostic_code::SchemaDdlAdmissionCode::MissingNextSchemaVersion
2366            }
2367            Self::StaleExpectedSchemaVersion => {
2368                diagnostic_code::SchemaDdlAdmissionCode::StaleExpectedSchemaVersion
2369            }
2370            Self::InvalidExpectedSchemaVersion => {
2371                diagnostic_code::SchemaDdlAdmissionCode::InvalidExpectedSchemaVersion
2372            }
2373            Self::InvalidNextSchemaVersion => {
2374                diagnostic_code::SchemaDdlAdmissionCode::InvalidNextSchemaVersion
2375            }
2376            Self::AcceptedSchemaChangeWithoutVersionBump => {
2377                diagnostic_code::SchemaDdlAdmissionCode::AcceptedSchemaChangeWithoutVersionBump
2378            }
2379            Self::EmptyVersionBump => diagnostic_code::SchemaDdlAdmissionCode::EmptyVersionBump,
2380            Self::VersionGap => diagnostic_code::SchemaDdlAdmissionCode::VersionGap,
2381            Self::VersionRollback => diagnostic_code::SchemaDdlAdmissionCode::VersionRollback,
2382            Self::FingerprintMethodMismatch => {
2383                diagnostic_code::SchemaDdlAdmissionCode::FingerprintMethodMismatch
2384            }
2385            Self::UnsupportedTransitionClass => {
2386                diagnostic_code::SchemaDdlAdmissionCode::UnsupportedTransitionClass
2387            }
2388            Self::PhysicalRunnerMissing => {
2389                diagnostic_code::SchemaDdlAdmissionCode::PhysicalRunnerMissing
2390            }
2391            Self::ValidationFailed => diagnostic_code::SchemaDdlAdmissionCode::ValidationFailed,
2392            Self::PublicationRaceLost => {
2393                diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost
2394            }
2395            Self::InvalidAddColumnDefault => {
2396                diagnostic_code::SchemaDdlAdmissionCode::InvalidAddColumnDefault
2397            }
2398            Self::InvalidAlterColumnDefault => {
2399                diagnostic_code::SchemaDdlAdmissionCode::InvalidAlterColumnDefault
2400            }
2401            Self::GeneratedIndexDropRejected => {
2402                diagnostic_code::SchemaDdlAdmissionCode::GeneratedIndexDropRejected
2403            }
2404            Self::SchemaRewriteRequiresMigration => {
2405                diagnostic_code::SchemaDdlAdmissionCode::SchemaRewriteRequiresMigration
2406            }
2407            Self::SchemaTransitionBudgetExceeded { .. } => {
2408                diagnostic_code::SchemaDdlAdmissionCode::SchemaTransitionBudgetExceeded
2409            }
2410            Self::GeneratedFieldDefaultChangeRejected => {
2411                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldDefaultChangeRejected
2412            }
2413            Self::GeneratedFieldNullabilityChangeRejected => {
2414                diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldNullabilityChangeRejected
2415            }
2416            Self::RowLayoutVersionExhausted => {
2417                diagnostic_code::SchemaDdlAdmissionCode::RowLayoutVersionExhausted
2418            }
2419        }
2420    }
2421}
2422
2423///
2424/// ErrorClass
2425/// Internal error taxonomy for runtime classification.
2426/// Not a stable API; may change without notice.
2427///
2428
2429#[repr(u8)]
2430#[derive(Clone, Copy, Eq, PartialEq)]
2431pub enum ErrorClass {
2432    Corruption,
2433    IncompatiblePersistedFormat,
2434    NotFound,
2435    Internal,
2436    Conflict,
2437    Unsupported,
2438    InvariantViolation,
2439}
2440
2441impl ErrorClass {
2442    /// Return a compact diagnostic code for this broad class and origin pair.
2443    #[must_use]
2444    pub const fn diagnostic_code(self, origin: ErrorOrigin) -> diagnostic_code::DiagnosticCode {
2445        match self {
2446            Self::Corruption if matches!(origin, ErrorOrigin::Store) => {
2447                diagnostic_code::DiagnosticCode::StoreCorruption
2448            }
2449            Self::Corruption => diagnostic_code::DiagnosticCode::RuntimeCorruption,
2450            Self::IncompatiblePersistedFormat => {
2451                diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
2452            }
2453            Self::NotFound if matches!(origin, ErrorOrigin::Store) => {
2454                diagnostic_code::DiagnosticCode::StoreNotFound
2455            }
2456            Self::NotFound => diagnostic_code::DiagnosticCode::RuntimeNotFound,
2457            Self::Internal => diagnostic_code::DiagnosticCode::RuntimeInternal,
2458            Self::Conflict => diagnostic_code::DiagnosticCode::RuntimeConflict,
2459            Self::Unsupported if matches!(origin, ErrorOrigin::Cursor) => {
2460                diagnostic_code::DiagnosticCode::QueryInvalidContinuationCursor
2461            }
2462            Self::Unsupported => diagnostic_code::DiagnosticCode::RuntimeUnsupported,
2463            Self::InvariantViolation if matches!(origin, ErrorOrigin::Store) => {
2464                diagnostic_code::DiagnosticCode::StoreInvariantViolation
2465            }
2466            Self::InvariantViolation => diagnostic_code::DiagnosticCode::RuntimeInvariantViolation,
2467        }
2468    }
2469}
2470
2471impl fmt::Debug for ErrorClass {
2472    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2473        write!(f, "{}", *self as u8)
2474    }
2475}
2476
2477///
2478/// ErrorOrigin
2479/// Internal origin taxonomy for runtime classification.
2480/// Not a stable API; may change without notice.
2481///
2482
2483#[repr(u8)]
2484#[derive(Clone, Copy, Eq, PartialEq)]
2485pub enum ErrorOrigin {
2486    Serialize,
2487    Store,
2488    Index,
2489    Identity,
2490    Query,
2491    Planner,
2492    Cursor,
2493    Recovery,
2494    Response,
2495    Executor,
2496    Interface,
2497}
2498
2499impl ErrorOrigin {
2500    /// Return the compact diagnostic origin for this internal origin.
2501    #[must_use]
2502    pub const fn diagnostic_origin(self) -> diagnostic_code::ErrorOrigin {
2503        match self {
2504            Self::Serialize => diagnostic_code::ErrorOrigin::Serialize,
2505            Self::Store => diagnostic_code::ErrorOrigin::Store,
2506            Self::Index => diagnostic_code::ErrorOrigin::Index,
2507            Self::Identity => diagnostic_code::ErrorOrigin::Identity,
2508            Self::Query => diagnostic_code::ErrorOrigin::Query,
2509            Self::Planner => diagnostic_code::ErrorOrigin::Planner,
2510            Self::Cursor => diagnostic_code::ErrorOrigin::Cursor,
2511            Self::Recovery => diagnostic_code::ErrorOrigin::Recovery,
2512            Self::Response => diagnostic_code::ErrorOrigin::Response,
2513            Self::Executor => diagnostic_code::ErrorOrigin::Executor,
2514            Self::Interface => diagnostic_code::ErrorOrigin::Interface,
2515        }
2516    }
2517}
2518
2519impl fmt::Debug for ErrorOrigin {
2520    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2521        write!(f, "{}", *self as u8)
2522    }
2523}