Reference
Error Reference
RFC 9457 error responses from CoreSDK.
Error Reference
CoreSDK returns application/problem+json errors following RFC 9457.
Every error includes a trace_id linking it to the corresponding OTEL span.
Error shape
{
"type": "https://coresdk.dev/errors/<error-code>",
"title": "Human-readable title",
"status": 401,
"detail": "Specific message for this occurrence",
"instance": "/api/orders/ord_7mP3",
"trace_id": "01HX7KQMB4NWE9P6T2JS0RY3ZV",
"tenant": "acme",
"timestamp": "2026-03-19T14:22:01Z"
}Error codes
| Code | Status | Cause |
|---|---|---|
jwt-missing | 401 | No Authorization header |
jwt-invalid | 401 | Signature verification failed |
jwt-expired | 401 | Token past exp claim |
jwt-unknown-key | 401 | kid not in JWKS |
policy-denied | 403 | Rego policy returned allow = false |
tenant-not-found | 403 | Tenant ID not provisioned |
rate-limit-exceeded | 429 | Per-tenant rate limit hit |
internal-error | 500 | CoreSDK internal error |
Policy denied error
403 errors include the policy action and evaluation context:
{
"type": "https://coresdk.dev/errors/policy-denied",
"title": "Authorization Denied",
"status": 403,
"detail": "Action 'orders:write' denied for role 'guest'",
"instance": "/api/orders",
"trace_id": "01HX7KQMB4NWE9P6T2JS0RY3ZV",
"tenant": "acme",
"policy": {
"action": "orders:write",
"policy_package": "coresdk.authz",
"user_role": "guest"
}
}Python — raising RFC 9457 errors
from coresdk.errors._rfc9457 import ProblemDetailError
raise ProblemDetailError(
type_uri="https://coresdk.dev/errors/policy-denied",
title="Authorization Denied",
status=403,
detail="Action 'orders:write' denied for role 'guest'",
)Rust — returning RFC 9457 responses
use coresdk_engine::ProblemDetail;
// In an Axum handler:
return ProblemDetail::unauthorized("Missing bearer token").into_response();Custom error mapper
Override the default error shape:
use coresdk_engine::error::{ErrorMapper, ProblemDetail, AuthError};
struct MyMapper;
impl ErrorMapper for MyMapper {
fn map(&self, err: AuthError) -> ProblemDetail {
match err {
AuthError::PolicyDenied(d) => ProblemDetail::new("policy-denied")
.status(403)
.title("Access Denied")
.detail(format!("You don't have permission to {}", d.action)),
_ => ProblemDetail::default_for(err),
}
}
}from coresdk.error import ErrorMapper, ProblemDetail, AuthError
class MyMapper(ErrorMapper):
def map(self, err: AuthError) -> ProblemDetail:
if isinstance(err, AuthError.PolicyDenied):
return (
ProblemDetail.new("policy-denied")
.status(403)
.title("Access Denied")
.detail(f"You don't have permission to {err.action}")
)
return ProblemDetail.default_for(err)import "github.com/coresdk/sdk/error"
type MyMapper struct{}
func (m *MyMapper) Map(err sdkerror.AuthError) sdkerror.ProblemDetail {
if denied, ok := err.(sdkerror.PolicyDenied); ok {
return sdkerror.NewProblemDetail("policy-denied").
Status(403).
Title("Access Denied").
Detail(fmt.Sprintf("You don't have permission to %s", denied.Action))
}
return sdkerror.DefaultFor(err)
}import { ErrorMapper, ProblemDetail, AuthError } from "@coresdk/sdk/error";
const myMapper: ErrorMapper = {
map(err: AuthError): ProblemDetail {
if (err.code === "policy-denied") {
return ProblemDetail.new("policy-denied")
.status(403)
.title("Access Denied")
.detail(`You don't have permission to ${err.action}`);
}
return ProblemDetail.defaultFor(err);
},
};Client-side handling
let res = client.get("/api/orders").send().await?;
if !res.status().is_success() {
let problem: serde_json::Value = res.json().await?;
// problem["type"], problem["title"], problem["detail"], problem["trace_id"]
eprintln!("[{}] {}: {}", problem["trace_id"], problem["title"], problem["detail"]);
}import httpx
res = httpx.get("/api/orders")
if not res.is_success:
problem = res.json()
# problem["type"], problem["title"], problem["detail"], problem["trace_id"]
print(f"[{problem['trace_id']}] {problem['title']}: {problem['detail']}")res, err := http.Get("/api/orders")
if err != nil {
log.Fatal(err)
}
if res.StatusCode >= 400 {
var problem map[string]interface{}
json.NewDecoder(res.Body).Decode(&problem)
// problem["type"], problem["title"], problem["detail"], problem["trace_id"]
log.Printf("[%s] %s: %s", problem["trace_id"], problem["title"], problem["detail"])
}const res = await fetch("/api/orders");
if (!res.ok) {
const problem = await res.json();
// problem.type, problem.title, problem.detail, problem.trace_id
console.error(`[${problem.trace_id}] ${problem.title}: ${problem.detail}`);
}The trace_id lets you look up the full request trace in your observability backend.