The 15 bugs AI coding assistants generate over and over (and a scanner that catches them) A developer created AINAScan, a deterministic AST scanner that detects 15 structural bug patterns commonly generated by AI coding assistants like GPT-4, Claude, Gemini, and Copilot. The scanner catches issues such as fake async functions, missing database writes, and stub skeletons, and includes upstream sanitizer detection to reduce false positives. Tests on 10 repos with 100k+ GitHub stars yielded zero false positives on legitimate code. AI coding assistants are fast. They're also surprisingly consistent at making the same class of structural mistakes. After scanning hundreds of AI-generated files, I kept seeing the same patterns: Pattern 1: MISSING WRITE AI generates a save function that never actually saves def save user data : validate data return {"status": "saved"} no INSERT, no UPDATE, nothing Pattern 2: FAKE ASYNC async keyword with no await anywhere async def fetch data url : return requests.get url synchronous, blocks the event loop Pattern 3: STUB SKELETON Placeholder that looks complete but does nothing def analyze sentiment text : return {} zero logic These aren't random bugs. They're structural patterns that appear across languages and models — GPT-4, Claude, Gemini, Copilot. The AI writes code that looks correct at a glance but breaks at runtime. The problem: existing scanners weren't designed for this. Bandit and Semgrep catch security vulnerabilities. They don't check whether your save user actually saves. AINAScan — a deterministic AST scanner with: | Pattern | What it catches | |---|---| MISSING WRITE | save/store function with no DB write | FAKE ASYNC | async def with no await | STUB SKELETON | function that just returns {} or None | DEAD CALL RESULT | calls 3 services, ignores all return values | HARDCODED TABLE | 40-key dict replacing what should be a DB query | INPUT OUTPUT DISCONNECTED | params never used in function body | TRIVIAL IF CHAIN | 7+ elif branches with no DB lookup | MOCK PATTERN | MagicMock in production code | EMPTY EXCEPT | except: pass swallowing errors silently | MISSING ERROR HANDLING | external API calls with no try/catch | TRIVIAL ASSERT | assert True in tests | TODO PLACEHOLDER | TODO/FIXME left in production | PARAM SHADOW | parameter shadowed by local variable | SHORT PASSTHROUGH | wrapper that adds no value | CONST SQL NO PARAM | SQL WHERE with hardcoded value | curl -X POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan \ -H "X-API-Key: vg free test" \ -F "file=@your agent.py" Response looks like: { "passed": false, "block count": 2, "warn count": 1, "issues": { "kind": "MISSING WRITE", "severity": "BLOCK", "line": 12, "detail": "function 'save user' claims to save but contains no DB write call" } } name: VibeGuard Security Scan on: pull request jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: Moonsehwan/aina-vibeguard-action@v1 with: api-key: ${{ secrets.VIBEGUARD KEY }} fail-on-block: 'true' One thing I spent a lot of time on: upstream sanitizer detection. Before flagging a path traversal issue, the scanner checks 60 lines before the sink for guard patterns: if '..' in path: return 400 scanner sees this filepath = open path and downgrades BLOCK to WARN This cut the false positive rate on well-maintained open source repos Django, FastAPI, celery from ~40% down to near zero. I also tested on 10 repos with 100k+ GitHub stars — 0 false positives on legitimate code. vg free test Questions welcome — especially curious what vibe-coding patterns others are seeing in their AI-generated codebases.