Skip to main content

AgentError

Struct AgentError 

Source
pub struct AgentError { /* private fields */ }
Expand description

Main error type with security-conscious design.

§Key Properties

  • All context is zeroized on drop (including source errors)
  • External display reveals minimal information
  • Internal logging uses structured data with explicit lifetimes
  • No implicit conversions from stdlib errors
  • Hot paths are zero-allocation where possible
  • Built-in constant-time error generation to reduce timing side-channels
  • Always-on error code obfuscation to resist fingerprinting

§Design Rationale - Error Constructors

We provide convenience constructors like config(), telemetry(), etc. even though ErrorCode already contains the category. This is intentional:

  1. Ergonomics: AgentError::config(code, ...) is clearer than AgentError::new(code, ...)
  2. Future extensibility: Different subsystems may need different context types
  3. Type safety: Prevents mixing categories (compile-time check vs runtime enum match)
  4. Grep-ability: Engineers can search for “::telemetry(” to find all telemetry errors

The redundancy is acceptable because it improves maintainability and reduces the chance of errors being created with mismatched code/category pairs.

Implementations§

Source§

impl AgentError

Source

pub fn with_retry(self) -> Self

Mark this error as retryable (transient failure)

Source

pub fn with_metadata( self, key: &'static str, value: impl Into<Cow<'static, str>>, ) -> Self

Add tracking metadata (correlation IDs, session tokens, etc.)

MUTATES IN PLACE - no cloning, no wasted allocations.

Source

pub fn with_timing_normalization(self, target_duration: Duration) -> Self

Normalize timing to prevent timing side-channel attacks.

This sleeps until target_duration has elapsed since error creation, ensuring that error responses take a consistent amount of time regardless of which code path failed.

§Use Cases
  • Authentication failures (prevent user enumeration)
  • Sensitive file operations (prevent path existence probing)
  • Cryptographic operations (prevent timing attacks on key material)
§Example
use palisade_errors::{AgentError, definitions};
use std::time::Duration;

fn authenticate(user: &str, pass: &str) -> palisade_errors::Result<()> {
    // Fast path: invalid username
    if !user_exists(user) {
        return Err(
            AgentError::config(definitions::CFG_VALIDATION_FAILED, "auth", "Invalid credentials")
                .with_timing_normalization(Duration::from_millis(100))
        );
    }
     
    // Slow path: password hash check
    if !check_password(user, pass) {
        return Err(
            AgentError::config(definitions::CFG_VALIDATION_FAILED, "auth", "Invalid credentials")
                .with_timing_normalization(Duration::from_millis(100))
        );
    }
     
    Ok(())
}

Both error paths now take at least 100ms, preventing attackers from distinguishing between “user doesn’t exist” and “wrong password”.

§Performance Note

This adds a sleep to the error path, which is acceptable since errors are not the hot path. The slight performance cost is worth the security benefit for sensitive operations.

§Limitations
  • Not async-safe: Blocks the thread. Use in sync contexts only.
  • Coarse precision: OS scheduling affects accuracy (1-15ms jitter).
  • Partial protection: Only normalizes error return timing, not upstream operations.
  • Observable side-channels: Network timing, cache behavior, DB queries remain.

This provides defense-in-depth against timing attacks but is not a complete solution.

Source

pub const fn is_retryable(&self) -> bool

Check if this error can be retried

Source

pub const fn code(&self) -> &ErrorCode

Get error code

Source

pub const fn category(&self) -> OperationCategory

Get operation category

Source

pub fn age(&self) -> Duration

Get the time elapsed since this error was created.

Useful for metrics and debugging, but should NOT be exposed externally as it could leak timing information.

Source

pub fn internal_log(&self) -> InternalLog<'_>

Create structured internal log entry with explicit lifetime.

§Critical Security Property

The returned InternalLog borrows from self and CANNOT outlive this error. This is intentional and enforces that sensitive data is consumed immediately by the logger and cannot be retained.

§Usage Pattern
let err = AgentError::config(
    definitions::CFG_PARSE_FAILED,
    "test",
    "test details"
);
let log = err.internal_log();
// logger.log_structured(log);  // log dies here
// err is dropped, all data zeroized

The short lifetime prevents accidental retention in log buffers, async contexts, or background threads.

Source

pub fn with_internal_log<F, R>(&self, f: F) -> R
where F: FnOnce(&InternalLog<'_>) -> R,

Alternative logging pattern for frameworks that need callback-style.

This enforces immediate consumption and prevents accidental retention:

err.with_internal_log(|log| {
    // logger.write(log.code(), log.operation());
    // log is destroyed when this closure returns
});
Source

pub fn config( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, ) -> Self

Create a configuration error

Source

pub fn config_sensitive( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, sensitive: impl Into<Cow<'static, str>>, ) -> Self

Create a configuration error with sensitive context

Source

pub fn deployment( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, ) -> Self

Create a deployment error

Source

pub fn telemetry( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, ) -> Self

Create a telemetry error

Source

pub fn correlation( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, ) -> Self

Create a correlation error

Source

pub fn response( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, ) -> Self

Create a response error

Source

pub fn logging( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, ) -> Self

Create a logging error

Source

pub fn platform( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, ) -> Self

Create a platform error

Source

pub fn io_operation( code: ErrorCode, operation: impl Into<Cow<'static, str>>, details: impl Into<Cow<'static, str>>, ) -> Self

Create an I/O operation error

Source

pub fn from_io_path( code: ErrorCode, operation: impl Into<Cow<'static, str>>, path: impl Into<Cow<'static, str>>, error: Error, ) -> Self

Wrap io::Error with explicit context, keeping path and error separate.

This prevents conflation of:

  • Error kind (semi-sensitive, reveals error type)
  • Path (sensitive, reveals filesystem structure)

By keeping them separate, logging systems can choose to handle them differently (e.g., hash paths but log error kinds).

Source

pub async fn with_timing_normalization_async( self, target_duration: Duration, ) -> Self

Async-safe timing normalization for non-blocking contexts.

Unlike with_timing_normalization, this uses async sleep primitives and won’t block the executor thread. Essential for Tokio/async-std runtimes.

§Example
async fn authenticate(user: &str, pass: &str) -> Result<Session> {
    let result = check_credentials(user, pass).await;
     
    if let Err(e) = result {
        // Normalize timing without blocking executor
        return Err(
            e.with_timing_normalization_async(Duration::from_millis(100)).await
        );
    }
     
    result
}
§Runtime Support
  • Requires either tokio or async-std feature
  • Tokio takes precedence if both are enabled
  • Will not compile without at least one async runtime feature

Trait Implementations§

Source§

impl Debug for AgentError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for AgentError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

External display - sanitized for untrusted viewers. Zero-allocation formatting.

Format: “{Category} operation failed [{permanence}] ({ERROR-CODE})”

Example: “Configuration operation failed [permanent] (E-CFG-100)”

This provides:

  • Operation domain (for troubleshooting)
  • Retry semantics (for automation)
  • Error code (for tracking)

Without revealing:

  • Internal paths or structure
  • Validation logic
  • User identifiers
  • Configuration values
  • Timing information
Source§

impl Drop for AgentError

Source§

fn drop(&mut self)

Panic-safe drop with explicit zeroization order.

Marked #[inline(never)] to prevent optimization that could skip zeroization.

Source§

impl Error for AgentError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.