systemprompt-agent 0.9.0

Agent-to-Agent (A2A) protocol for systemprompt.io AI governance: streaming, JSON-RPC models, task lifecycle, .well-known discovery, and governed agent orchestration.
Documentation
//! Typed error hierarchy for the `systemprompt-agent` crate.
//!
//! Public APIs return concrete `thiserror`-derived enums instead of
//! `anyhow::Error` so that downstream callers can match on error variants
//! without string parsing.

use systemprompt_identifiers::TaskId;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum RowParseError {
    #[error("Missing required field: {field}")]
    MissingField { field: String },

    #[error("Invalid datetime for field '{field}'")]
    InvalidDatetime { field: String },

    #[error("JSON parse error for field '{field}': {source}")]
    JsonParse {
        field: String,
        #[source]
        source: serde_json::Error,
    },
}

#[derive(Debug, Error)]
pub enum TaskError {
    #[error("Task UUID missing from database row")]
    MissingTaskUuid,

    #[error("Agent name not found for task {task_id}")]
    MissingAgentName { task_id: TaskId },

    #[error("Context ID missing from database row")]
    MissingContextId,

    #[error("Invalid task state: {state}")]
    InvalidTaskState { state: String },

    #[error(transparent)]
    RowParse(#[from] RowParseError),

    #[error("Metadata parse error: {0}")]
    InvalidMetadata(#[from] serde_json::Error),

    #[error("Empty task ID provided")]
    EmptyTaskId,

    #[error("Invalid task ID format: {id}")]
    InvalidTaskIdFormat { id: String },

    #[error("Message ID missing from database row")]
    MissingMessageId,

    #[error("Tool name missing for tool execution")]
    MissingToolName,

    #[error("Tool call ID missing for tool execution")]
    MissingCallId,

    #[error("Created timestamp missing from database")]
    MissingCreatedTimestamp,

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

#[derive(Debug, Error)]
pub enum ContextError {
    #[error("Context UUID missing from database row")]
    MissingUuid,

    #[error("Context name missing from database row")]
    MissingName,

    #[error("User ID missing from database row")]
    MissingUserId,

    #[error(transparent)]
    RowParse(#[from] RowParseError),

    #[error("Role serialization error: {0}")]
    RoleSerialization(#[from] serde_json::Error),

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

#[derive(Debug, Error)]
pub enum ArtifactError {
    #[error("Artifact UUID missing from database row")]
    MissingUuid,

    #[error("Artifact type missing from database row")]
    MissingType,

    #[error("Context ID missing for artifact")]
    MissingContextId,

    #[error(transparent)]
    RowParse(#[from] RowParseError),

    #[error("Invalid tool response schema: expected {expected}, found keys: {actual_keys:?}")]
    InvalidSchema {
        expected: &'static str,
        actual_keys: Vec<String>,
        #[source]
        source: serde_json::Error,
    },

    #[error("Metadata parse error: {0}")]
    InvalidMetadata(#[from] serde_json::Error),

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

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

    #[error("Metadata validation error: {0}")]
    MetadataValidation(String),
}

#[derive(Debug, Error)]
pub enum ProtocolError {
    #[error("Tool name missing in tool call")]
    MissingToolName,

    #[error("Tool result error flag is required but was not provided")]
    MissingErrorFlag,

    #[error("Message ID missing")]
    MissingMessageId,

    #[error("Request ID missing")]
    MissingRequestId,

    #[error("Latency value missing or invalid")]
    InvalidLatency,

    #[error("Validation failed: {0}")]
    ValidationFailed(String),

    #[error("JSON parse error: {0}")]
    JsonParse(#[from] serde_json::Error),

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

#[derive(Debug, Error)]
pub enum AgentError {
    #[error("Task error: {0}")]
    Task(#[from] TaskError),

    #[error("Context error: {0}")]
    Context(#[from] ContextError),

    #[error("Artifact error: {0}")]
    Artifact(#[from] ArtifactError),

    #[error("A2A protocol error: {0}")]
    Protocol(#[from] ProtocolError),

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

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

    #[error("repository init: {0}")]
    Init(String),

    #[error("server: {0}")]
    Server(String),

    #[error("webhook: {0}")]
    Webhook(String),

    #[error("config: {0}")]
    Config(String),

    #[error("http: {0}")]
    Http(#[from] reqwest::Error),

    #[error("agent not found: {0}")]
    NotFound(String),

    #[error("spawn failed: {0}")]
    Spawn(String),

    #[error("lifecycle: {0}")]
    Lifecycle(String),

    #[error("validation: {0}")]
    Validation(String),

    #[error("sqlx error: {0}")]
    Sqlx(#[from] sqlx::Error),

    #[error("io: {0}")]
    Io(#[from] std::io::Error),

    #[error("internal: {0}")]
    Internal(String),

    #[error("services config: {0}")]
    ServicesConfig(#[from] systemprompt_loader::ConfigLoadError),
}

pub type AgentResult<T> = Result<T, AgentError>;

impl From<AgentError> for systemprompt_traits::RepositoryError {
    fn from(err: AgentError) -> Self {
        match err {
            AgentError::Sqlx(e) => Self::Database(Box::new(e)),
            other => Self::Database(other.to_string().into()),
        }
    }
}