Skip to main content

ConfigValidationError

Enum ConfigValidationError 

Source
#[non_exhaustive]
pub enum ConfigValidationError { EmptyServerName, EmptyServerVersion, EmptyToolName(usize), EmptyTableName(usize), InlineSecretRejected, AmbiguousToolKind(usize), EmptyBackendBaseUrl, MalformedBackendBaseUrlRef, MalformedBackendAuthRef(String), EmptyConfigSlotField(usize), SecretSlotCarriesTestedValue(usize), }
Expand description

Semantic-validation errors surfaced by crate::config::ServerConfig::validate.

Per Phase 83 review R8 — the Default impls on ServerConfig and its sub-sections deliberately allow from_toml to succeed even when required fields are missing (so partial configs can be merged programmatically). The crate::config::ServerConfig::validate entry-point catches these gaps at parse time and surfaces them as a typed enum variant per rule.

The enum is #[non_exhaustive] — match callers must include a wildcard arm so additional rules can be added without a breaking change.

§Examples

use pmcp_server_toolkit::ConfigValidationError;

// Each variant has a precise `Display` describing the rule violated.
let err = ConfigValidationError::EmptyServerName;
assert_eq!(err.to_string(), "server.name must be non-empty");
let err = ConfigValidationError::EmptyToolName(3);
assert_eq!(err.to_string(), "[[tools]] entry at index 3 has empty name");

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

EmptyServerName

[server] name is missing or whitespace-only.

§

EmptyServerVersion

[server] version is missing or whitespace-only.

§

EmptyToolName(usize)

[[tools]] entry at index has an empty / whitespace-only name.

§

EmptyTableName(usize)

[[database.tables]] entry at index has an empty / whitespace-only name.

§

InlineSecretRejected

Per Phase 83 Plan 06 review R9: [code_mode].token_secret was given as an inline literal (e.g. token_secret = "raw-string") instead of the env:VAR_NAME reference form, and the dev-only escape hatch allow_inline_token_secret_for_dev was not set. Inline literals in committed configs leak HMAC signing keys; the toolkit defaults to rejecting them.

§

AmbiguousToolKind(usize)

Per Phase 90 Plan 02 (D-01, T-90-02-04): a [[tools]] entry declares more than one mutually-exclusive tool kind. A tool is EITHER a SQL tool (sql), a single-call HTTP tool (path/method), OR a script tool (script) — never a mixture. The ambiguity is rejected rather than resolved by a silent precedence rule. The usize is the entry index.

§

EmptyBackendBaseUrl

Per Phase 90 gap-closure (GAP 3 / WR-02): a [backend] block is present but its base_url is empty / whitespace-only (or the base_url key was omitted, defaulting to "" via #[serde(default)]). Without this parse-time check a typo’d or missing base_url would validate cleanly and then surface late as an opaque DispatchError::Connector("invalid base URL") at the first backend request. Rejecting it here turns that late opaque failure into an actionable, field-naming error.

§

MalformedBackendBaseUrlRef

[backend].base_url is REFERENCE-shaped but does not name exactly one environment variable — the empty ${} form, or a multi-placeholder composition like "${SCHEME}://${HOST}". The grammar (crate::env_ref::parse_env_ref) resolves one whole-value ${VAR} / env:VAR reference; it does not interpolate inside a larger string, so no environment could ever satisfy such a value. Without this check the config loads cleanly and every boot fails with an UnresolvedBaseUrlRef naming an empty variable.

§

MalformedBackendAuthRef(String)

A [backend.auth] credential field is REFERENCE-shaped but does not name exactly one environment variable — the empty ${} form, a multi-placeholder composition like "${SCHEME}://${HOST}", or a non-portable name like "${TFL-APP-KEY}" (a ${...} name must match [A-Za-z0-9_]+; env:VAR remains the escape hatch for exotic names). The String is the offending field path within [backend.auth] — e.g. "token", "password", "query_params.app_key".

This is the credential sibling of Self::MalformedBackendBaseUrlRef, and it exists because the two paths resolve UNSET references differently on purpose: a credential resolves an unset variable to the empty string so an optional credential is OMITTED. A MALFORMED reference is not an unset variable — no environment can ever satisfy it — so applying the omission rule to it silently sent every backend request UNAUTHENTICATED, with no error and no log line. Refusing it at load time turns that into an actionable, field-naming failure before the server ever boots.

The message names the FIELD only and deliberately never echoes the configured value: a malformed value is by definition not a resolvable reference, so it may well be a mistyped literal secret.

§

EmptyConfigSlotField(usize)

Per Phase 120 Plan 04 (PKG-03): a [[config_slots]] entry at index has an empty / whitespace-only key or name. A slot declaration whose key names no config path — or whose name names no environment variable — claims coverage it cannot deliver, and the package side would compare against an empty string.

The sibling “unrecognized kind” check is NOT here: kind is the closed crate::config::ConfigSlotKind enum, so serde rejects an unknown discriminator at PARSE time (naming the accepted set) before validate() is ever called.

§

SecretSlotCarriesTestedValue(usize)

A [[config_slots]] entry at index is kind = "secret" but carries a tested_value. Identity-bearing slots structurally carry no value — the tested_value field on a secret declaration is the one place a REAL credential could sit in a config that is served but never packed (the pack-time agreement gate only runs on packaging), so the rule is enforced at validation time rather than trusted as prose. The message deliberately does not echo the value.

Trait Implementations§

Source§

impl Debug for ConfigValidationError

Source§

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

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

impl Display for ConfigValidationError

Source§

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

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

impl Error for ConfigValidationError

1.30.0 · 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
Source§

impl From<ConfigValidationError> for ToolkitError

Source§

fn from(source: ConfigValidationError) -> Self

Converts to this type from the input type.

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

Source§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

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

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 = !

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.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more