Recurring Bug Patterns (R1-R5)
Relevant source files
The following files were used as context for generating this wiki page:
Recurring bug patterns (identified as R1 through R5) represent errors that have appeared in at least two out of three major source projects within this repository. While they may not have the universal prevalence of Core Patterns, they serve as high-signal warnings for specific technology stacks, particularly those involving SQLAlchemy, Postgres, Browser APIs, and External SDKs.
These patterns bridge the gap between stack-agnostic errors and highly specific project bugs. They are categorized to allow developers to apply relevant checks based on the technologies used in their current project scope.
Sources: RECURRING-PATTERNS.md:1-7, README.md:9-16
R1: SQLAlchemy AsyncSession Lifecycle
This pattern addresses the complexities of using AsyncSession and async_sessionmaker in SQLAlchemy. Improper lifecycle management often leads to MissingGreenlet exceptions or silent data corruption.
Key Lifecycle Risks
- Rollback Sensitivity: Accessing ORM object attributes after a
session.rollback()triggers errors. Primitives must be extracted before the rollback. - Concurrent Sharing: Sharing a single
AsyncSessionacross multiple concurrent tasks viaasyncio.gatherleads to race conditions. - Stale Data after Commit: Commits refresh only the Primary Key (PK); subsequent attribute access requires an explicit
session.refresh(row).
The following diagram illustrates the lifecycle risks when performing concurrent database operations with a shared session.
Implementation Recommendations
| Scenario | Recommended Action |
|---|---|
| Concurrent Tasks | Each task must open its own session using async with sessionmaker(). |
| Post-Rollback Access | Extract IDs/primitives before rollback or use session.expunge(obj). |
| Loop Commits | Call await session.refresh(row) before re-accessing attributes. |
| Locking with Joinedload | Use contains_eager instead of joinedload. |
Sources: RECURRING-PATTERNS.md:39-49
R2: Pagination Without Secondary Tiebreakers
Non-deterministic ordering occurs when sorting by non-unique fields (like timestamp or score) without a secondary unique tiebreaker. In Postgres, ties can result in random row order between pages, causing rows to be skipped or duplicated.
Sources: RECURRING-PATTERNS.md:65-72
Required Implementation: Every ORDER BY used with LIMIT, OFFSET, or cursor pagination must include a unique tiebreaker, typically the Primary Key (e.g., ORDER BY created_at DESC, id ASC).
Sources: RECURRING-PATTERNS.md:79-83
R3: Browser API and DOM Quirks
This pattern covers non-obvious behaviors in Browser APIs that differ from standard backend logic. These often manifest as silent UX failures or intermittent data loss.
Common Browser API Issues
- Protocol Handoff:
window.open("mailto:...")returnsnullin Chrome. This is not a popup blocker failure but a standard protocol handoff; code should not treat thisnullas an error. - Clipboard Security:
navigator.clipboardrequires HTTPS and will throw errors on HTTP without a try/catch block. - Resource Leaks:
URL.createObjectURLrequires explicitURL.revokeObjectURLduring component cleanup (e.g.,useEffectcleanup) to prevent memory leaks.
Sources: RECURRING-PATTERNS.md:98-115, BY-STACK/browser-policy.md:14-20
R4: External SDK Exception Completeness
Developers often catch base exceptions that are too narrow, allowing specific subclasses or BaseException variants (like asyncio.CancelledError) to crash the application or result in incorrect state tracking.
SDK Safety Checklist
- Catch Base Classes: Catch the documented SDK base class (e.g.,
anthropic.APIError). - Order Subclasses: Place specific subclasses (e.g.,
RateLimitError) before the general base class. - Handle Startup: Wrap SDK initialization (e.g., Stripe/OAuth) in try/except blocks to prevent a single bad environment variable from crashing the entire server boot process.
Sources: RECURRING-PATTERNS.md:158-166
R5: Filter Scope and Terminal States
Filter errors occur when logic is either too narrow (excluding valid cases) or fails to account for terminal states in recurring jobs.
Terminal State Failures in Cron Jobs
A recurring job must ensure that processed items leave the filter set. Relying on heuristics like lead_id IS NULL fails when terminal states (like spam) also leave that field null, leading to infinite loops.
Filter Priority Rules
Security and exclusion filters (deny/block/fraud) must always execute before business logic or overrides (force-include/whitelist). Failure to order these correctly allows blocked entities to bypass security if they trigger a force-send keyword.
Sources: BY-STACK/cron-jobs.md:69-75, RECURRING-PATTERNS.md:195-201
Summary of Recurring Patterns
| ID | Pattern Name | Primary Stack | Severity |
|---|---|---|---|
| R1 | SQLAlchemy Async Lifecycle | Python / SQLAlchemy | Medium |
| R2 | Pagination Tiebreakers | Postgres / SQL | Medium |
| R3 | Browser API Quirks | Frontend / JS | Medium |
| R4 | SDK Exception Handling | External APIs | Medium |
| R5 | Filter Scope/Cron States | Backend / Cron | High |
Sources: RECURRING-PATTERNS.md:10-210
Understanding these patterns is critical for maintaining robust systems, as they often bypass standard unit tests that do not simulate concurrent database access, browser security policies, or external API failures. Proper implementation of R1-R5 ensures that the system fails gracefully and remains deterministic under production load.
Sources: TESTING-PATTERNS.md:14-23, MIGRATION-NOTES.md:97-105