Sanitizing Agent Inputs: Prompt Validation Before Inference
Protect PydanticAI agents against prompt injection, jailbreaks, and malformed inputs using typed input contracts and pre-inference sanitization filters.
Published on • September 11, 2026
AI Assistant

Prompt injection remains the single biggest security vulnerability in modern LLM applications (OWASP LLM01). When malicious users inject control characters, override system instructions, or embed hidden prompt instructions inside user inputs, agents can be tricked into leaking sensitive data or executing unauthorized tool calls.
Securing agents requires a Defense-in-Depth input validation pipeline that sanitizes and validates user prompts before they are sent to the model for inference.
Input Sanitization Strategy
A complete input validation pipeline performs three sequential checks on every user prompt:
- Structural & Character Sanitization: Stripping dangerous control sequences, zero-width characters, and excessive whitespace.
- Type & Length Validation: Enforcing maximum length limits and validating expected payload types (JSON, UUID, email format).
- Prompt Injection Classifier: Scoring the prompt against known adversarial injection signatures using lightweight local classifiers before invoking expensive LLMs.
[User Input] --> [Character Sanitizer] --> [Pydantic Contract] --> [Injection Detector] --> [LLM Engine]
Input Sanitization with PydanticAI
PydanticAI introduces type-safe agent definitions where dependencies and input parameters are strictly typed using Pydantic models.
from pydantic import BaseModel, Field, field_validator
from pydantic_ai import Agent, RunContext
import re
class UserQueryInput(BaseModel):
query: str = Field(..., min_length=5, max_length=1000, description="User search or action query")
user_id: str = Field(..., pattern=r"^USR-\d{6}$")
@field_validator("query")
@classmethod
def sanitize_prompt_text(cls, v: str) -> str:
# Strip system instruction override patterns and control tokens
forbidden_patterns = [
r"ignore previous instructions",
r"system prompt override",
r"you are now in developer mode",
r"<\|im_start\|>",
r"<\|im_end\|>"
]
sanitized = v.strip()
for pattern in forbidden_patterns:
if re.search(pattern, sanitized, re.IGNORECASE):
raise ValueError(f"Input contains prohibited prompt injection signature: '{pattern}'")
# Remove null bytes and non-printable control characters
sanitized = re.sub(r"[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]", "", sanitized)
return sanitized
# Instantiate PydanticAI Agent with validated input context
agent = Agent(
"gemini-1.5-flash",
deps_type=UserQueryInput,
system_prompt="You are an enterprise data assistant. Answer queries strictly using provided tools."
)
@agent.system_prompt
def add_user_context(ctx: RunContext[UserQueryInput]) -> str:
return f"Active User: {ctx.deps.user_id}. Query: {ctx.deps.query}"
Running the Safe Execution Pipeline
def process_user_request(raw_user_id: str, raw_query: str):
try:
# 1. Pydantic validation handles sanitization and pattern matching
validated_input = UserQueryInput(user_id=raw_user_id, query=raw_query)
# 2. Execute PydanticAI Agent safely
result = agent.run_sync(validated_input.query, deps=validated_input)
return result.data
except ValueError as validation_err:
# Catch prompt injection attempts cleanly before model invocation
return f"Request Rejected: {validation_err}"
Key Security Takeaways
- Pre-Inference Rejection: Filtering out bad input at the application layer saves model token costs and eliminates execution exposure.
- Never Concatenate Raw Input into System Prompts: Keep user inputs strictly separated in user message turns or validated context objects.
- Audit Logging: Log rejected inputs and source IPs to identify active scanning or probing attempts against your agent endpoints.
To learn more about type-safe agent design, dependency injection, and model evaluation with PydanticAI, check out the PydanticAI Framework Documentation.