Skip to main content

Problem

Struct Problem 

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

An ACME error, rendered as an RFC 8555 §6.7 application/problem+json document:

{ "type": "urn:ietf:params:acme:error:malformed", "detail": "…", "status": 400 }

This struct represents ACME protocol errors that are returned to clients in the standardized RFC 8555 problem+json format. It implements the IntoResponse trait to convert errors into proper HTTP responses.

§ACME Protocol Compliance

Following RFC 8555, each error has:

  • A type field with URN format identifying the error type
  • A detail field with human-readable description
  • A status field with HTTP status code

§Error Types

  • malformed: Request format issues (HTTP 400)
  • bad_nonce: Invalid or expired nonce (HTTP 400)
  • unauthorized: Signature verification failures (HTTP 401)
  • server_internal: Internal server errors (HTTP 500)
  • account_does_not_exist: Referenced account not found (HTTP 400)
  • order_not_ready: Finalize attempted on a non-ready order (HTTP 403)
  • bad_csr: Unacceptable finalize CSR (HTTP 400)
  • unsupported_identifier: Unsupported newOrder identifier type (HTTP 400)
  • rejected_identifier: Identifier refused by server policy (HTTP 403)
  • access_denied: Request blocked by a filter (HTTP 403)
  • external_account_required: newAccount missing a required EAB (HTTP 400)
  • already_revoked: Certificate already revoked (HTTP 400)
  • bad_revocation_reason: Unsupported CRLReason code (HTTP 400)
  • key_change_conflict: keyChange new key belongs to another account (HTTP 409)
  • unsupported_media_type: body was not application/jose+json (HTTP 415)
  • method_not_allowed: resource read with the wrong method, e.g. a bare GET (HTTP 405)
  • not_found: nothing routed at this path (HTTP 404)

§Usage

Used both as the AcmeRequest extractor’s rejection and as the error arm of handler results, so every failure reaches the client in the shape ACME clients expect.

§Owned details

detail is a Cow<'static, str> rather than a &'static str: most call sites pass a literal (borrowed, no allocation), but the filter subsystem needs to name the offending value — “identifier evil.example.com is denied by policy” — which can only be built at runtime.

Implementations§

Source§

impl Problem

Source

pub fn malformed(detail: impl Into<Cow<'static, str>>) -> Self

The request was unacceptable for some reason (bad JSON, base64, JWS shape, unexpected payload, wrong algorithm…). HTTP 400.

Source

pub fn bad_nonce(detail: impl Into<Cow<'static, str>>) -> Self

The client sent an unacceptable anti-replay nonce (unknown or expired). The client should retry with a fresh nonce. HTTP 400.

Source

pub fn unauthorized(detail: impl Into<Cow<'static, str>>) -> Self

The client lacks authorization to perform the request — here, a JWS signature that fails verification. HTTP 401.

Source

pub fn server_internal(detail: impl Into<Cow<'static, str>>) -> Self

The server experienced an internal failure (e.g. the database was unreachable). HTTP 500.

Source

pub fn account_does_not_exist(detail: impl Into<Cow<'static, str>>) -> Self

The request referenced an account that does not exist. HTTP 400.

Source

pub fn order_not_ready(detail: impl Into<Cow<'static, str>>) -> Self

A finalize was attempted on an order that is not in the ready state (RFC 8555 §7.4). HTTP 403.

Source

pub fn bad_csr(detail: impl Into<Cow<'static, str>>) -> Self

The CSR in a finalize request was unacceptable (unparsable, its identifiers do not match the order, or a filter refused one of the names it requests). HTTP 400.

Source

pub fn unsupported_identifier(detail: impl Into<Cow<'static, str>>) -> Self

A newOrder identifier used a type the server does not support (only dns is supported here). HTTP 400.

Source

pub fn rejected_identifier(detail: impl Into<Cow<'static, str>>) -> Self

The server will not issue for an identifier it otherwise supports, because policy refuses it (RFC 8555 §6.7). Raised by the filter subsystem at newOrder. HTTP 403.

Source

pub fn access_denied(detail: impl Into<Cow<'static, str>>) -> Self

The request was blocked by a connection-level filter (IP allowlist, reverse DNS…), or a challenge responder answered with something that is not the key authorization. HTTP 403.

RFC 8555 has no dedicated “blocked by policy” code, so this reuses the unauthorized type — but with 403 rather than the 401 that Problem::unauthorized returns for a failed signature check, since no credential could make this request succeed. RFC 8555 §8.3’s own example uses the same type for an http-01 body mismatch.

Source

pub fn connection(detail: impl Into<Cow<'static, str>>) -> Self

The server could not reach the client’s validation target — TCP refused, no route, a redirect chain that never terminated. HTTP 400.

One of the four challenge-validation error types of RFC 8555 §6.7. The client can fix the situation and retry with a new order.

Source

pub fn dns(detail: impl Into<Cow<'static, str>>) -> Self

A DNS query the server needed failed or returned nothing (RFC 8555 §6.7). HTTP 400.

Distinct from Problem::incorrect_response: this says the lookup did not produce an answer, not that the answer was wrong.

Source

pub fn incorrect_response(detail: impl Into<Cow<'static, str>>) -> Self

The validation target answered, but not with what the challenge requires — a TXT record that does not match, a certificate without the expected acmeIdentifier extension (RFC 8555 §6.7). HTTP 403.

Source

pub fn tls(detail: impl Into<Cow<'static, str>>) -> Self

A TLS-level failure while validating a tls-alpn-01 challenge — no ALPN negotiated, a handshake alert, no certificate presented (RFC 8555 §6.7). HTTP 400.

Source

pub fn external_account_required(detail: impl Into<Cow<'static, str>>) -> Self

The server requires External Account Binding (RFC 8555 §6.7 / §7.3.4) but the request did not include one. HTTP 400.

Source

pub fn already_revoked(detail: impl Into<Cow<'static, str>>) -> Self

The certificate identified by a POST /revokeCert request has already been revoked (RFC 8555 §7.6). HTTP 400.

Source

pub fn bad_revocation_reason(detail: impl Into<Cow<'static, str>>) -> Self

The reason a POST /revokeCert request gave is not one of the CRLReason codes RFC 8555 §7.6 permits (RFC 5280 §5.3.1, excluding the reserved code 7). HTTP 400.

Source

pub fn key_change_conflict(detail: impl Into<Cow<'static, str>>) -> Self

The new key in a keyChange (RFC 8555 §7.3.5) request is already associated with a different account. HTTP 409.

RFC 8555 defines no dedicated error type for this case, so this reuses the malformed urn — mirroring how Problem::access_denied already reuses unauthorized’s urn under a different status. Unlike every other Problem, the caller also attaches a Location header naming the conflicting account (RFC 8555 §7.3.5), which requires building the Response by hand rather than through IntoResponse (see to_value).

Source

pub fn unsupported_media_type(detail: impl Into<Cow<'static, str>>) -> Self

The request body did not carry Content-Type: application/jose+json. HTTP 415.

RFC 8555 §6.2 makes the media type mandatory and names the status itself: “If a request does not meet this requirement, then the server MUST return a response with status code 415 (Unsupported Media Type)”. It defines no error type for the case, so this reuses malformed’s urn under a different status — the same pattern as Problem::access_denied and Problem::key_change_conflict.

Source

pub fn payload_too_large(detail: impl Into<Cow<'static, str>>) -> Self

The request body was larger than server.max_body_bytes. HTTP 413.

Distinct from malformed on purpose: the body was never read, so nothing is known about whether it was a well-formed JWS, and telling a client its JWS is malformed would send it rebuilding the one thing that is not the problem. RFC 8555 defines no type for this, so it reuses malformed’s urn under its own status — the same pattern as Problem::unsupported_media_type.

Source

pub fn service_unavailable(detail: impl Into<Cow<'static, str>>) -> Self

The server is at capacity and refused the request without doing any of the work. HTTP 503.

Deliberately not rateLimited/429: that type says “you asked too often”, which is a statement about the client, and here the client may have made its first request of the day. RFC 8555 defines no type for server-side saturation, so this reuses serverInternal’s urn under a different status — the same pattern as Problem::access_denied, Problem::key_change_conflict and Problem::unsupported_media_type.

The caller attaches Retry-After; every ACME client already understands it from the rate-limit case.

Source

pub fn method_not_allowed(detail: impl Into<Cow<'static, str>>) -> Self

The resource exists but not for this HTTP method — in practice, a bare GET of a resource that RFC 8555 §6.3 requires be read with POST-as-GET. HTTP 405.

§6.3 pins both halves: “if the server receives a GET request, it MUST return an error with status code 405 (Method Not Allowed) and type malformed”. axum’s own method-not-allowed response carries the right status but an empty body, so this supplies the problem document.

Source

pub fn already_replaced(detail: impl Into<Cow<'static, str>>) -> Self

A newOrder named a predecessor certificate that another order already claims to replace (RFC 9773 §7.4). HTTP 409.

The one status RFC 9773 pins by name: §5 says the server “MUST return an HTTP 409 (Conflict) with a problem document of type alreadyReplaced” — unlike the other §5 checks, which only say “SHOULD reject”.

Source

pub fn unsupported_contact(detail: impl Into<Cow<'static, str>>) -> Self

A contact URL used a scheme this server does not support (RFC 8555 §7.3). HTTP 400.

Source

pub fn invalid_contact(detail: impl Into<Cow<'static, str>>) -> Self

A contact URL was of a supported scheme but not usable — a mailto: carrying hfields or more than one address (RFC 8555 §7.3). HTTP 400.

Source

pub fn user_action_required(detail: impl Into<Cow<'static, str>>) -> Self

The client must take an out-of-band action before the request can succeed — here, agreeing to the terms of service (RFC 8555 §7.3.3). HTTP 403.

The caller attaches a Link: <tos-url>;rel="terms-of-service" header, as §6.7 requires for this type.

Source

pub fn rate_limited(detail: impl Into<Cow<'static, str>>) -> Self

The client asked for more than this server will do for it at once (RFC 8555 §6.6). HTTP 429.

The caller attaches a Retry-After, which §6.6 recommends for this type: the limit is on work in flight, so waiting is what clears it.

Source

pub fn not_found(detail: impl Into<Cow<'static, str>>) -> Self

No resource is routed at the requested path. HTTP 404.

RFC 8555 defines no type for this either; malformed keeps an unknown path answering in the application/problem+json shape every other failure uses, rather than axum’s empty-bodied default.

Source§

impl Problem

Source

pub fn compound( status: StatusCode, detail: impl Into<Cow<'static, str>>, ) -> Self

Several errors at once, each attributed to its own identifier (RFC 8555 §6.7.1). Pair with Problem::with_subproblems.

The status is the caller’s to choose, since §6.7.1 puts no constraint on it and a compound of rejections (403) reads differently from a compound of malformed names (400).

Source

pub fn bad_signature_algorithm(detail: impl Into<Cow<'static, str>>) -> Self

The JWS was signed with an algorithm this server does not support (RFC 8555 §6.2). HTTP 400.

§6.2 requires the response to carry the supported list: “an algorithms field […] listing the JWS algorithms the server supports”, so the client can retry with one instead of guessing. Attached here rather than left to the caller, since the list is a property of this server’s verifier, not of the call site.

Source§

impl Problem

Source

pub fn status(&self) -> StatusCode

The HTTP status this problem renders as.

Exposed so a caller assembling a compound (RFC 8555 §6.7.1) can pick a status for the wrapper from the parts it is wrapping.

Source

pub fn detail(&self) -> &str

The human-readable detail, as text — for a caller that carries a problem into an error of its own. Reading it back out of Problem::to_value gives a JSON string, quotes included.

Source

pub fn with_identifier(self, identifier: &Identifier) -> Self

Attaches the identifier this problem is about (RFC 8555 §9.7.7).

Only meaningful on a problem destined to become a subproblem: §6.7.1 forbids the field at the top level, and Problem::to_value drops it there, so calling this on a problem that is then returned directly is a no-op rather than a violation.

Source

pub fn with_subproblems(self, subproblems: Vec<Problem>) -> Self

Attaches per-identifier failures (RFC 8555 §6.7.1).

§6.7.1: “Subproblems need not all have the same type, and they do not need to match the top level type.”

Source

pub fn with_extra(self, key: &str, value: Value) -> Self

Attaches a type-specific member, e.g. badSignatureAlgorithm’s algorithms list (RFC 8555 §6.2).

Source

pub fn to_value(&self) -> Value

The RFC 8555 problem document as a JSON value ({type, detail, status}, plus subproblems and any type-specific members when present).

Shared by IntoResponse (the response body) and callers that need to persist the same document — e.g. an order’s error field on a failed finalize renders exactly what the client is told.

identifier is deliberately absent: §6.7.1 makes it a subproblem-only field, and Problem::to_subproblem_value is where it appears.

Trait Implementations§

Source§

impl Debug for Problem

Source§

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

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

impl Display for Problem

Source§

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

The type and the detail, which is what an operator needs when a problem travels inside another error rather than out to a client.

Not the JSON — that is Problem::to_value, and a log line is not a place to put a document. Having any Display at all is what lets the error types that carry a Problem derive thiserror (ADR 0010).

Source§

impl Error for Problem

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 IntoResponse for Problem

Source§

fn into_response(self) -> Response

Create a response.

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<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

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<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

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

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