use std::path::PathBuf;
use thiserror::Error;
use crate::prompts::hints;
#[derive(Debug, Clone, Copy)]
pub struct RecoveryHint(pub &'static str);
impl std::fmt::Display for RecoveryHint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.0)
}
}
#[derive(Debug, Error)]
pub enum ClaudixError {
#[error("config invalid: {message}")]
ConfigInvalid {
message: String,
recovery: RecoveryHint,
},
#[error("embedding endpoint unreachable: {endpoint}")]
EmbeddingUnreachable {
endpoint: String,
#[source]
source: reqwest::Error,
recovery: RecoveryHint,
},
#[error("embedding endpoint timed out after {timeout_ms}ms: {endpoint}")]
EmbeddingTimedOut {
endpoint: String,
timeout_ms: u64,
recovery: RecoveryHint,
},
#[error("embedding endpoint requires auth (HTTP {status}): {endpoint}")]
EmbeddingAuthRejected {
endpoint: String,
status: u16,
recovery: RecoveryHint,
},
#[error("embedding endpoint returned HTTP {status}: {endpoint}")]
EmbeddingHttpStatus {
endpoint: String,
status: u16,
recovery: RecoveryHint,
},
#[error("schema version mismatch: store={store}, binary={binary}")]
SchemaMismatch {
store: u32,
binary: u32,
recovery: RecoveryHint,
},
#[error("dimension mismatch: store={store_dim}, model={model_dim}")]
DimensionMismatch {
store_dim: u16,
model_dim: u16,
recovery: RecoveryHint,
},
#[error("embedding model mismatch: store={store_model}, active={active_model}")]
EmbeddingModelMismatch {
store_model: String,
active_model: String,
recovery: RecoveryHint,
},
#[error("path traversal: {path:?} is outside project root")]
PathTraversal {
path: PathBuf,
recovery: RecoveryHint,
},
#[error("bundled assets missing for model {model_id}")]
BundledAssetsMissing {
model_id: String,
recovery: RecoveryHint,
},
#[error("embedding error: {0}")]
Embedding(String),
#[error("tree-sitter error: {0}")]
TreeSitter(String),
#[error("store error: {0}")]
Store(String),
#[error("mcp server error: {0}")]
Mcp(String),
#[error("serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("lance error: {0}")]
Lance(#[from] lancedb::Error),
#[error("not a git repository: {path:?}")]
NotAGitRepository {
path: PathBuf,
recovery: RecoveryHint,
},
#[error("git enumeration error: {0}")]
Git(String),
#[error("ignore pattern error: {0}")]
Ignore(#[from] ignore::Error),
#[error("http error: {0}")]
Http(#[from] reqwest::Error),
}
impl ClaudixError {
pub fn recovery_hint(&self) -> Option<&'static str> {
match self {
Self::ConfigInvalid { recovery, .. } => Some(recovery.0),
Self::EmbeddingUnreachable { recovery, .. } => Some(recovery.0),
Self::EmbeddingTimedOut { recovery, .. } => Some(recovery.0),
Self::EmbeddingAuthRejected { recovery, .. } => Some(recovery.0),
Self::EmbeddingHttpStatus { recovery, .. } => Some(recovery.0),
Self::SchemaMismatch { recovery, .. } => Some(recovery.0),
Self::DimensionMismatch { recovery, .. } => Some(recovery.0),
Self::EmbeddingModelMismatch { recovery, .. } => Some(recovery.0),
Self::NotAGitRepository { recovery, .. } => Some(recovery.0),
Self::PathTraversal { recovery, .. } => Some(recovery.0),
Self::BundledAssetsMissing { recovery, .. } => Some(recovery.0),
Self::Io(_) => Some(hints::IO_CHECK_DISK),
Self::Store(_) => Some(hints::STORE_DOCTOR),
Self::Lance(_) => Some(hints::LANCE_DOCTOR),
Self::Git(_) => Some(hints::GIT_ENUM_DOCTOR),
Self::Ignore(_) => Some(hints::IGNORE_PATTERN),
Self::Http(_) => Some(hints::HTTP_DOCTOR),
Self::Embedding(_) => Some(hints::EMBEDDING_GENERIC),
Self::TreeSitter(_) => Some(hints::TREE_SITTER_REINDEX),
Self::Serialization(_) => None,
Self::Mcp(_) => None,
}
}
pub fn is_endpoint_unavailable(&self) -> bool {
matches!(
self,
Self::EmbeddingUnreachable { .. }
| Self::EmbeddingTimedOut { .. }
| Self::EmbeddingAuthRejected { .. }
| Self::EmbeddingHttpStatus { .. }
)
}
}
pub type Result<T> = std::result::Result<T, ClaudixError>;