use thiserror::Error;
#[derive(Debug, Error)]
pub enum SessionError {
#[error("session event log I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("session event (de)serialization error: {0}")]
Serde(#[from] serde_json::Error),
#[error("session store database error: {0}")]
Db(#[from] zeph_db::SqlxError),
#[error("session not found: {0}")]
NotFound(String),
#[error("invalid fork point: {0}")]
InvalidForkPoint(String),
#[error("condensation range overlap: {0}")]
CondensationOverlap(String),
#[error("condensation summarization failed: {0}")]
Llm(#[from] zeph_llm::LlmError),
#[error("{}", describe_already_locked(path, *pid, *pid_alive))]
AlreadyLocked {
path: String,
pid: Option<u32>,
pid_alive: Option<bool>,
},
#[error("invalid blob hash (must be a non-empty hex string): {0:?}")]
InvalidBlobHash(String),
#[error("{0}")]
Integrity(String),
}
fn describe_already_locked(path: &str, pid: Option<u32>, pid_alive: Option<bool>) -> String {
match (pid, pid_alive) {
(Some(pid), Some(false)) => format!(
"session event log at {path} is already locked: the recorded holder pid {pid} is \
not running; the lock may be stale, or may have just been re-acquired by another \
process — verify before removing it. Not auto-recovered because breaking a live \
flock without holder cooperation is unsafe"
),
(Some(pid), _) => format!(
"session event log at {path} is already locked by another process (held by pid {pid})"
),
(None, _) => format!(
"session event log at {path} is already locked by another process (holder pid unknown)"
),
}
}