{"slug": "solving-the-claude-3-5-sonnet-hallucinations-in-a-fastapi-project", "title": "Solving the Claude 3.5 Sonnet hallucinations in a FastAPI project", "summary": "A developer spent four hours debugging a Pydantic ValidationError in a FastAPI project after Claude 3.5 Sonnet repeatedly insisted the schema was correct, only to find the AI had ignored a strict=True setting in the project's ConfigDict. The developer resolved the issue by casting the input rather than removing strict=True, and reported that switching from asking the AI for fixes to requesting minimal reproducible test scripts cut trial-and-error cycles by 70%.", "body_md": "# Solving the Claude 3.5 Sonnet hallucinations in a FastAPI project\n\nI spent four hours last Thursday chasing a `Pydantic ValidationError` that shouldn't have existed. I was using Claude 3.5 Sonnet inside [Cursor](https://promptcube3.com/en/tags/cursor/) for some AI pair programming on a messy FastAPI backend, and the AI kept insisting my schema was correct while the server kept crashing.\n\nThe fix ended up being a one-line change to a type hint, but the path to get there was a disaster of \"trusting the AI\" too much.\n\n## The loop of death with AI-generated schemas\n\nI was building a nested JSON response for a dashboard. I had a Pydantic model for `UserStats` and a wrapper model `DashboardResponse`. Every time I ran the server, I got this:\n\n`pydantic.error_wrappers.ValidationError: 1 validation error for DashboardResponse -> user_stats. Expected float, got str`\n\nThe wild part? I looked at the code, and it was clearly a `float`. I asked [Claude](https://promptcube3.com/en/tags/claude/) to fix it. It told me the code was already correct and that the error was likely coming from the database driver. I spent an hour debugging my PostgreSQL connection strings and querying the raw JSONB column. Everything was fine there.\n\nI fed the error back into the AI. It apologized and suggested I \"update my Pydantic version.\" I did. Still crashed. It then suggested the issue was a hidden character in the file. I deleted the line and rewrote it. Still crashed.\n\nThis is the \"hallucination loop\" of AI pair programming. The AI becomes so convinced of its own previous logic that it starts inventing phantom bugs in your environment rather than admitting it missed a detail in the code.\n\n## How I actually diagnosed the bottleneck\n\nI stopped asking the AI \"Why is this happening?\" and started using the AI to write a diagnostic script. Instead of letting it rewrite the production code, I told it to write a standalone `.py` script that simulated the exact data input causing the crash.\n\n``` python\n# The \"sanity check\" script that finally revealed the lie\nfrom pydantic import BaseModel\n\nclass UserStats(BaseModel):\n    uptime: float # The AI insisted this was fine\n\n# Simulated data from my DB\ntest_data = {\"uptime\": \"12.5\"} \n\ntry:\n    UserStats(**test_data)\nexcept Exception as e:\n    print(f\"Caught it: {e}\")\n```\n\nWhen I ran this, the error popped up instantly. The database was returning the number as a string. Now, normally, Pydantic coerces strings to floats. But I was using a strict type configuration in my `ConfigDict` that I'd forgotten about.\n\nThe AI had completely ignored the `strict=True` setting in my base model configuration while suggesting fixes. It was treating the code as if it were standard Pydantic, even though the project context clearly showed the strict requirement.\n\n## Fixing the workflow to avoid this again\n\nThe solution was simple: remove `strict=True` or cast the input. I went with the cast. But the real lesson was about how to actually collaborate with an LLM without getting gaslit.\n\nIf you're using [AI Models](https://promptcube3.com/en/category/aimodels/) for heavy lifting, you have to treat them like a junior developer who is incredibly confident but occasionally lies. Stop asking \"What's wrong?\" and start asking \"Write a minimal reproducible example of this failure.\"\n\n| Old Approach | New Approach | Result |\n\n| :--- | :--- | :--- |\n\n| Paste error → Ask for fix → Apply fix | Paste error → Request test script → Verify → Apply fix | 70% less \"trial and error\" cycles |\n\n| Trusting AI's environment diagnosis | Manually verifying versions/logs | No more wasted time \"updating\" packages |\n\n## Why a community beats a solo chat window\n\nThe most frustrating part of that four-hour spiral was that I felt like I was the only one hitting this specific Pydantic/Claude friction point. When I jumped into the [PromptCube homepage](https://promptcube3.com/en/) and started digging through how others were structuring their context windows, I realized the mistake was mine: I was providing too much irrelevant code and not enough \"ground truth\" (like the `ConfigDict` file).\n\nThe \"secret sauce\" isn't the model; it's the prompt structure. I found that by explicitly telling the AI to \"Cross-reference all suggested changes with the `config.py` file,\" the hallucinations dropped significantly.\n\nSharing these specific \"failure patterns\" through [Prompt Sharing](https://promptcube3.com/en/category/prompts/) helps everyone stop making the same mistakes. One person figures out that Claude struggles with strict Pydantic types, shares the prompt fix, and suddenly a hundred other devs save four hours of their Thursday.\n\n## My current AI pair programming stack\n\nI've stopped jumping between five different tools. Here is what actually works for me right now:\n\n1. **Cursor** for the primary IDE experience (the codebase indexing is non-negotiable).\n\n2. **Claude 3.5 Sonnet** for logic and refactoring.\n\n3. **GPT-4o** specifically for regex or very simple boilerplate where Sonnet over-engineers.\n\n4. **A dedicated \"Context\" folder** where I keep `.md` files describing my architectural decisions, so I can feed them to the AI when it starts hallucinating.\n\nIf you're still just copying and pasting errors into a browser tab, you're doing it the hard way. The jump from \"using an AI\" to \"AI pair programming\" is moving from a request-response loop to a shared-context workflow.\n\n[Next DEV.to needs a one-tap translation toggle for comments to actually feel global →](https://promptcube3.com/en/threads/9366/)", "url": "https://wpnews.pro/news/solving-the-claude-3-5-sonnet-hallucinations-in-a-fastapi-project", "canonical_source": "https://promptcube3.com/en/posts/9378/", "published_at": "2026-09-14 20:15:41+00:00", "updated_at": "2026-09-14 22:56:46.291788+00:00", "lang": "en", "topics": ["ai-tools", "large-language-models", "developer-tools"], "entities": ["Claude 3.5 Sonnet", "FastAPI", "Pydantic", "Cursor", "PromptCube"], "alternates": {"html": "https://wpnews.pro/news/solving-the-claude-3-5-sonnet-hallucinations-in-a-fastapi-project", "markdown": "https://wpnews.pro/news/solving-the-claude-3-5-sonnet-hallucinations-in-a-fastapi-project.md", "text": "https://wpnews.pro/news/solving-the-claude-3-5-sonnet-hallucinations-in-a-fastapi-project.txt", "jsonld": "https://wpnews.pro/news/solving-the-claude-3-5-sonnet-hallucinations-in-a-fastapi-project.jsonld"}}