python
37 lines · 7 steps
Redacting sensitive fields in FastAPI errors
A custom validation handler strips secret values from FastAPI's 422 responses before they leak to clients.
Explained by
highlit
1from fastapi import FastAPI, Request, status
2from fastapi.encoders import jsonable_encoder
3from fastapi.exceptions import RequestValidationError
4from fastapi.responses import JSONResponse
5
6app = FastAPI()
7
8SENSITIVE_FIELDS = {"password", "token", "secret", "ssn", "card_number", "cvv"}
9REDACTED = "[redacted]"
10
11
12def _redact_error(error: dict) -> dict:
13 loc = error.get("loc", ())
14 is_sensitive = any(str(part).lower() in SENSITIVE_FIELDS for part in loc)
15 if not is_sensitive:
16 return error
17
18 cleaned = {
19 "type": error.get("type"),
20 "loc": loc,
21 "msg": error.get("msg"),
22 }
23 ctx = error.get("ctx")
24 if ctx is not None:
25 cleaned["ctx"] = {k: REDACTED for k in ctx}
26 if "input" in error:
27 cleaned["input"] = REDACTED
28 return cleaned
29
30
31@app.exception_handler(RequestValidationError)
32async def redacting_validation_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
33 redacted = [_redact_error(err) for err in exc.errors()]
34 return JSONResponse(
35 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
36 content=jsonable_encoder({"detail": redacted}),
37 )
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Validation errors echo back user input, which can expose passwords or tokens unless you scrub them.
- 2Overriding an exception handler lets you reshape framework error responses centrally.
- 3Redact by field name and drop raw input rather than trying to filter values.
Related explainers
python
from collections.abc import MutableMapping class CaseInsensitiveDict(MutableMapping):
Building a case-insensitive dict in Python
data structures
abstract base classes
dunder methods
Intermediate
8 steps
php
<?php namespace App\Http\Requests\DataObjects;
Typed request DTOs in Laravel
data-transfer-object
validation
immutability
Intermediate
6 steps
go
package middleware import ( "net/http"
Role-based access control middleware in Gin
middleware
authorization
closures
Intermediate
7 steps
go
func UserDashboardCache(rdb *redis.Client, ttl time.Duration) gin.HandlerFunc { return func(c *gin.Context) { claims, ok := c.Get("claims") if !ok {
Per-user response caching in Gin with Redis
middleware
caching
redis
Advanced
9 steps
python
from datetime import timedelta from flask import Blueprint, current_app, make_response, redirect, request, url_for from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired
How signed remember-me cookies work in Flask
authentication
signed-cookies
session-management
Intermediate
8 steps
rust
use std::{collections::HashSet, sync::Arc}; use axum::{ body::Body,
Feature-flag middleware in Axum
middleware
async
shared-state
Advanced
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/redacting-sensitive-fields-in-fastapi-errors-explained-python-c5ca/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.