stemedb/ai-lookup/patterns/error-handling.md
jordan 1ce4004807 feat: Complete Phase 2 (The Cortex) - query, lens, and API layers
This commit adds the read path (Cortex) to complement the write path (Spine):

## Crates
- stemedb-api: HTTP API with axum + utoipa OpenAPI
  - /v1/assert, /v1/query, /v1/epoch, /v1/skeptic, /v1/trace, /v1/audit
  - Metered endpoints with quota enforcement
  - Ed25519 signature verification
- stemedb-lens: Truth resolution lenses
  - RecencyLens, ConsensusLens, ConfidenceLens
  - VoteAwareConsensusLens (Ballot Box pattern)
  - TrustAwareAuthorityLens (The Hive pattern)
  - SkepticLens (conflict analysis)
  - EpochAwareLens (paradigm-safe queries)
- stemedb-query: Query engine with materialized views

## Storage Extensions
- VoteStore: Vote aggregation with cached counts
- TrustRankStore: Agent reputation with decay
- AuditStore: Query audit trail
- IndexStore: SP/P/S index structures
- SupersessionStore: Epoch supersession chains

## SDKs
- sdk/go/steme: Go HTTP client with Ed25519 signing
- sdk/go/adk: ADK-Go tools for AI agents

## Documentation
- Updated CLAUDE.md, architecture.md, roadmap.md
- New ai-lookup entries for all services
- Use case docs for consumer health intelligence
- Arena roadmap for simulation advancement

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 13:22:44 -07:00

2.1 KiB

Error Handling Pattern

Last Updated: 2026-01-31 Confidence: High

Summary

StemeDB uses thiserror for library errors with context chains. No panics in production code. All fallible operations return Result<T, E>.

Key Facts:

  • Library code: thiserror for custom error types — ALL error enums MUST use #[derive(thiserror::Error)]
  • Binary code: anyhow for error chaining
  • Never use unwrap(), expect(), panic!() in production
  • Add context with .context("what we were doing")?
  • NEVER use manual impl Display + impl Error for error types — use thiserror derives instead

Error types in workspace (all use thiserror):

  • stemedb-core/src/serde.rsSerdeError
  • stemedb-wal/src/error.rsQuarantineError
  • stemedb-storage/src/error.rsStorageError
  • stemedb-ingest/src/error.rsIngestError
  • stemedb-query/src/error.rsQueryError
  • stemedb-api/src/error.rsApiError

The Pattern

use thiserror::Error;

#[derive(Debug, Error)]
pub enum StemeError {
    #[error("assertion not found: {0:?}")]
    NotFound(Hash),

    #[error("invalid signature for agent {agent:?}")]
    InvalidSignature { agent: AgentId },

    #[error("storage error: {0}")]
    Storage(#[from] sled::Error),

    #[error("serialization error: {0}")]
    Serialization(String),
}

// Usage with context
fn load_assertion(&self, hash: &Hash) -> Result<Assertion, StemeError> {
    let bytes = self.store
        .get(hash.as_bytes())
        .context("failed to read assertion from store")?
        .ok_or(StemeError::NotFound(*hash))?;

    rkyv::from_bytes(&bytes)
        .map_err(|e| StemeError::Serialization(e.to_string()))
}

Error Categories

Type Description Example
NotFound Data doesn't exist Missing assertion
InvalidSignature Crypto verification failed Tampered assertion
Storage Underlying KV error Disk full
Serialization Encode/decode failed Corrupt data