Error Handling
ServiceError variants, HTTP status codes, and JSON response format
Error Design
All KMS errors use a centralized ServiceError enum that implements IntoResponse for Axum. Every error path produces a consistent JSON body with an HTTP status code, a snake_case error code, and an optional details string.
JSON Response Format
"error": "snake_case_error_code",
"details": "Optional human-readable description" // may be null
}
The error field is always present and uses a stable snake_case identifier suitable for programmatic matching. The details field provides context for debugging but may be omitted or null.
ServiceError Variants
ServiceError Enum
The Rust enum that maps each variant to an HTTP status code and JSON body.
InvalidInput(String), // → 400
InvalidChallenge(String), // → 400 or 401
InvalidSignature(String), // → 401
PolicyViolation(String), // → 403
RateLimited, // → 429
PolicyNotReady, // → 503
InternalError(String), // → 500
}
impl IntoResponse for ServiceError {
fn into_response(self) -> Response {
// maps variant → (StatusCode, Json { error, details })
}
}
Error Flows by Endpoint
POST /challenge
400 InvalidInput
Missing or malformed peerId in the request body.
429 RateLimited
The peerId already has MAX_PENDING_CHALLENGES active challenges.
503 PolicyNotReady
The KMS policy hasn’t been loaded yet. The node should retry.
500 InternalError
Challenge store write failure (e.g. Redis connection error).
POST /get-key
400 InvalidInput
Missing or malformed fields in the request body (challengeId, quote, signature).
400/401 InvalidChallenge
Challenge not found, expired, or peerId mismatch. 400 for not-found/expired, 401 for peerId mismatch.
401 InvalidSignature
Ed25519 signature verification failed against the peerId’s public key.
403 PolicyViolation
TDX quote measurements don’t match the attestation policy allowlists.
503 PolicyNotReady
Policy not yet loaded (startup race).
500 InternalError
dstack communication failure, quote parsing error, or key derivation failure.
POST /attest
400 InvalidInput
Missing or malformed nonce in the request body.
500 InternalError
dstack quote generation failure (socket error, TDX driver error).
GET /health
503 PolicyNotReady
Returns 503 until the attestation policy is successfully loaded. Load balancers should use this to determine readiness.
200 OK
Service is ready to accept requests. Policy is loaded and challenge store is operational.
Example Responses
400 InvalidInput
Content-Type: application/json
{
"error": "invalid_input",
"details": "missing field: peerId"
}
401 InvalidSignature
Content-Type: application/json
{
"error": "invalid_signature",
"details": "Ed25519 signature verification failed"
}
403 PolicyViolation
Content-Type: application/json
{
"error": "policy_violation",
"details": "RTMR3 value 'a1b2c3...' not in allowed values for profile 'locked-read-only'"
}
429 RateLimited
Content-Type: application/json
{
"error": "rate_limited",
"details": null
}
503 PolicyNotReady
Content-Type: application/json
{
"error": "policy_not_ready",
"details": "attestation policy not yet loaded"
}
500 InternalError
Content-Type: application/json
{
"error": "internal_error",
"details": "failed to communicate with dstack"
}