pub enum ApiError {
Unauthorized,
NotFound {
code: &'static str,
message: String,
},
BadRequest {
code: &'static str,
message: String,
},
TooManyRequests {
code: &'static str,
message: String,
},
Timeout {
message: String,
},
Internal(String),
}Expand description
Every failure the API can report, with the JSON body clients receive.
§Deviation from the design
Phase 2 design §7 specified a nested body carrying the status twice
({"error": {"code", "message", "status"}}). The body here is flat —
{"error": "<code>", "message": "<text>"} — and the status is only in the
HTTP status line. Rationale: the status is already authoritative on the wire
and repeating it in the body is a second source of truth that can disagree
with the status line. Flattening also means an agent parses one object with
no branch before it can read the machine code.
error is a stable machine code drawn from a closed set
(ApiError::code) and is what a client should branch on. The codes are the
ones §7 enumerates; message is prose for humans and is not guaranteed to
be stable across releases.
§Why NotFound and BadRequest carry their own code
Both map to a single HTTP status, so the status alone cannot tell an agent
what went wrong: 404 covers both “no such index” and “no such node”, and
400 covers a missing param, an unparseable param, and a bad body. The
status says what kind of failure; the code says which, which is what
lets a client retry sensibly without matching on prose.
Variants§
No Authorization header, wrong scheme, unknown key, or wrong secret.
NotFound
The request was well-formed but names something that does not exist.
Fields
BadRequest
The request itself is malformed (missing field, bad value).
Fields
TooManyRequests
The request was authenticated and well-formed, but a limit of
the caller’s plan refused it (paid-path S3). A distinct status
— not a 400 with a quota-flavoured code — because 429 is
the one answer a client can act on without parsing prose: stop,
wait for the window the message names, retry then.
Fields
Timeout
A query outlived its plan’s per-request time limit.
Fields
Internal(String)
Storage, indexing, or an unexpected internal failure.
Implementations§
Source§impl ApiError
impl ApiError
Sourcepub const INTERNAL_CLIENT_MESSAGE: &'static str = "internal error"
pub const INTERNAL_CLIENT_MESSAGE: &'static str = "internal error"
The only message a client ever sees for an internal failure.
The Internal variant’s payload is operator detail — host
filesystem paths, rusqlite error text, store file names — that
helps the person running the server and must never reach a
client (fleet release gate F-1). ApiError::body logs the
payload server-side and renders this generic text instead.
Sourcepub fn unknown_index(id: &str) -> Self
pub fn unknown_index(id: &str) -> Self
404 unknown_index: {id} does not name a database in the data dir.
Sourcepub fn unknown_node(id: &str) -> Self
pub fn unknown_node(id: &str) -> Self
404 unknown_node: the index loaded, but the node is not in the graph.
Sourcepub fn no_path(from: &str, to: &str) -> Self
pub fn no_path(from: &str, to: &str) -> Self
404 no_path: no typed path connects the two nodes.
Not in the §7 table, which does not cover a well-formed path query
whose answer is “these two symbols are unrelated”. The CLI rejects the
same case rather than returning an empty path, so 404 is preserved and
given its own code instead of being folded into unknown_index or
reported as an empty 200.
Sourcepub fn missing_param(name: &str) -> Self
pub fn missing_param(name: &str) -> Self
400 missing_param: a required query parameter is absent or empty.
Sourcepub fn invalid_param(name: &str, reason: &str) -> Self
pub fn invalid_param(name: &str, reason: &str) -> Self
400 invalid_param: a query parameter is present but unusable.
Sourcepub fn invalid_body(reason: &str) -> Self
pub fn invalid_body(reason: &str) -> Self
400 invalid_body: the request body is not the expected JSON shape.
Sourcepub fn invalid_repo_path(reason: &str) -> Self
pub fn invalid_repo_path(reason: &str) -> Self
400 invalid_repo_path: repo_path is missing or is not a directory.
Sourcepub fn invalid_repo_url(reason: &str) -> Self
pub fn invalid_repo_url(reason: &str) -> Self
400 invalid_repo_url: repo_url is unparseable, not a public
HTTPS URL, or its clone failed (paid-path S2). The sibling of
invalid_repo_path for the hosted ingest mode: one code per
ingest field, so a client can branch on which source was refused
without matching on prose.
Sourcepub fn index_quota(plan: &str, limit: usize, current: usize) -> Self
pub fn index_quota(plan: &str, limit: usize, current: usize) -> Self
429 index_quota_exceeded: the key already holds its plan’s
active-index limit. Names the limit and the current count, so
the caller knows exactly what must be deleted or expire before
another creation can succeed.
Sourcepub fn query_quota(plan: &str, limit: u64, used: u64, month: &str) -> Self
pub fn query_quota(plan: &str, limit: u64, used: u64, month: &str) -> Self
429 query_quota_exceeded: the key has used its plan’s
monthly query allowance. The month is the UTC calendar month
(see crate::limits); naming it tells the caller when the
quota resets without a second request to ask.
Sourcepub fn file_quota(plan: &str, limit: usize) -> Self
pub fn file_quota(plan: &str, limit: usize) -> Self
429 file_quota_exceeded: the tree holds more files than the
plan’s per-index cap. The count is a lower bound (the walk
stops just past the limit), so the message says “more than”.
Sourcepub fn index_concurrency(plan: &str, limit: usize) -> Self
pub fn index_concurrency(plan: &str, limit: usize) -> Self
429 index_concurrency_exceeded: the key already runs its
plan’s allowance of concurrent index operations.
Sourcepub fn request_timeout(plan: &str, secs: u64) -> Self
pub fn request_timeout(plan: &str, secs: u64) -> Self
504 request_timeout: the query did not finish within the
plan’s per-request limit.
Sourcepub fn internal(reason: impl Into<String>) -> Self
pub fn internal(reason: impl Into<String>) -> Self
500 from any internal failure, carrying reason.
Sourcepub fn body(&self) -> Value
pub fn body(&self) -> Value
Body as a JSON value; exposed for tests and for asserting shape.
This is the single render point for client responses
(IntoResponse goes through it), so the Internal redaction
lives here rather than at the variant’s many construction sites:
the detail is written to the server’s stderr and the client
receives ApiError::INTERNAL_CLIENT_MESSAGE. Every other
variant’s message is prose about the caller’s own request and
renders as before.
Trait Implementations§
Source§impl Error for ApiError
impl Error for ApiError
1.30.0 · Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Source§impl IntoResponse for ApiError
impl IntoResponse for ApiError
Source§fn into_response(self) -> Response
fn into_response(self) -> Response
Auto Trait Implementations§
impl Freeze for ApiError
impl RefUnwindSafe for ApiError
impl Send for ApiError
impl Sync for ApiError
impl Unpin for ApiError
impl UnsafeUnpin for ApiError
impl UnwindSafe for ApiError
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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