Cursor often generates Go code with goroutine leaks, unbuffered channels that deadlock, and missing sync primitives because safe concurrency patterns are harder to learn from training data. By adding .cursor/rules/ with Go concurrency best practices, referencing existing channel patterns with @file, and prompting with explicit safety requirements, you get Cursor to produce properly synchronized Go code with context cancellation, WaitGroups, and channel management.
Fixing concurrency issues in Go code from Cursor
Go's concurrency model with goroutines and channels is powerful but error-prone. Common issues in AI-generated Go code include goroutine leaks from unread channels, deadlocks from unbuffered channels, race conditions from shared state access, and missing context cancellation. This tutorial configures Cursor to generate safe concurrent Go code with proper synchronization.
Prerequisites
- Cursor installed with a Go project (Go 1.21+)
- Understanding of goroutines, channels, and sync primitives
- Familiarity with context.Context patterns
- Go race detector available (go test -race)
Step-by-step guide
Create a Go concurrency safety rule
Create a Go concurrency safety rule
Add a project rule that specifies Go concurrency best practices. Include patterns for channel management, goroutine lifecycle, and context propagation. Be explicit about dangerous anti-patterns that Cursor commonly generates.
1---2description: Go concurrency safety patterns3globs: "*.go"4alwaysApply: true5---67# Go Concurrency Rules89## Channel Safety:10- ALWAYS use buffered channels when the sender should not block11- ALWAYS close channels from the sender side, never the receiver12- NEVER read from a nil channel (blocks forever)13- Use select with a default case to prevent blocking14- Use context.Done() channel for cancellation in select statements1516## Goroutine Lifecycle:17- EVERY goroutine must have a clear termination path18- ALWAYS use sync.WaitGroup or errgroup.Group to wait for goroutines19- ALWAYS pass context.Context as first parameter to goroutine functions20- NEVER launch goroutines that outlive their parent function without lifecycle management2122## Shared State:23- PREFER channels over shared memory for communication24- When using shared state, ALWAYS protect with sync.Mutex or sync.RWMutex25- Use atomic operations for simple counters26- NEVER pass a mutex by value2728## Anti-Patterns (NEVER):29```go30go func() { /* no way to stop this */ }() // goroutine leak31ch := make(chan int) // unbuffered + single goroutine = deadlock risk32time.Sleep(time.Second) // NEVER use sleep for synchronization33```Expected result: Cursor generates Go concurrent code with proper channel buffering, goroutine lifecycle management, and context cancellation.
Generate a safe worker pool pattern
Generate a safe worker pool pattern
Worker pools are one of the most common concurrency patterns in Go and one of the most error-prone when generated by AI. Prompt Cursor with explicit requirements for graceful shutdown, error propagation, and goroutine lifecycle.
1@go-concurrency.mdc23Create a worker pool in Go with these requirements:41. Configurable number of workers (numWorkers int)52. Input channel for jobs, output channel for results63. context.Context for cancellation — all workers stop when context is cancelled74. errgroup.Group to wait for all workers and propagate the first error85. Graceful shutdown: drain remaining jobs before exiting96. No goroutine leaks: every goroutine must exit when the pool stops1011The job type is func(ctx context.Context) (Result, error).12Include a NewPool constructor, Start, Submit, and Shutdown methods.Pro tip: Always mention 'no goroutine leaks' in Go concurrency prompts. This triggers Cursor to add proper cleanup code and context cancellation handling.
Expected result: Cursor generates a worker pool with context cancellation, errgroup for error propagation, and proper channel cleanup.
Audit existing concurrent code for safety issues
Audit existing concurrent code for safety issues
Use Cursor Chat with @codebase to scan your Go project for common concurrency bugs. Cursor can identify goroutine leaks, potential deadlocks, and race conditions from code analysis alone.
1@go-concurrency.mdc @codebase23Audit this Go project for concurrency safety issues. Check for:41. Goroutines launched without context cancellation support52. Unbuffered channels that could deadlock63. Channels that are never closed (goroutine leak risk)74. Shared variables accessed from multiple goroutines without sync85. time.Sleep used for synchronization instead of proper primitives96. Missing WaitGroup or errgroup for goroutine lifecycle1011For each issue, show the file, the problematic code, and the fix.Expected result: Cursor lists concurrency safety issues across your Go project with specific file locations and corrected code.
Generate a safe pub/sub pattern with channels
Generate a safe pub/sub pattern with channels
Pub/sub with channels requires careful management of subscriber registration, message broadcasting, and subscriber cleanup. Prompt Cursor with explicit requirements for each of these concerns.
1@go-concurrency.mdc23Create a thread-safe in-memory pub/sub broker in Go:41. Subscribe(topic string) returns a <-chan Message and an unsubscribe func52. Publish(topic string, msg Message) sends to all subscribers non-blocking63. Use sync.RWMutex for the subscriber map74. Buffered subscriber channels (buffer size 100)85. Drop messages if subscriber channel is full (non-blocking send)96. Unsubscribe closes the channel and removes from the map107. Close() method that unsubscribes all and prevents new subscriptions1112Ensure no goroutine leaks and no panics from sending on closed channels.Expected result: Cursor generates a pub/sub broker with proper mutex locking, buffered channels, non-blocking sends, and cleanup methods.
Test generated code with the race detector
Test generated code with the race detector
After generating concurrent Go code, use Cursor to generate tests that exercise the concurrent paths. Then run them with the race detector. Ask Cursor to create tests that specifically stress concurrent access patterns.
1@go-concurrency.mdc @pkg/worker/pool.go23Generate tests for the worker pool that exercise concurrency:41. TestPool_ConcurrentSubmit — submit 1000 jobs from 50 goroutines52. TestPool_ContextCancellation — cancel context mid-processing63. TestPool_GracefulShutdown — submit jobs then shutdown, verify all complete74. TestPool_ErrorPropagation — submit a failing job, verify error returned85. TestPool_NoGoroutineLeaks — use goleak to verify no leaked goroutines910All tests must pass with go test -race -count=100.11Use t.Parallel() where appropriate.Expected result: Cursor generates concurrency-focused tests that validate safety with the race detector and leak detection.
Complete working example
1---2description: Go concurrency safety patterns3globs: "*.go"4alwaysApply: true5---67# Go Concurrency Rules89## Channel Safety:10- ALWAYS use buffered channels when the sender should not block11- ALWAYS close channels from the sender side, never the receiver12- NEVER read from a nil channel (blocks forever)13- Use select with default case to prevent blocking when appropriate14- ALWAYS include context.Done() in select statements for cancellation1516## Goroutine Lifecycle:17- EVERY goroutine must have a clear termination path18- ALWAYS use sync.WaitGroup or errgroup.Group to wait for goroutines19- ALWAYS pass context.Context as first parameter20- NEVER launch fire-and-forget goroutines without lifecycle management2122## Shared State:23- PREFER channels over shared memory for goroutine communication24- Protect shared state with sync.Mutex or sync.RWMutex25- Use atomic operations for simple counters (atomic.Int64)26- NEVER pass a mutex by value (use pointer or embed in struct)2728## Correct Worker Pattern:29```go30func worker(ctx context.Context, jobs <-chan Job, results chan<- Result) {31 for {32 select {33 case <-ctx.Done():34 return35 case job, ok := <-jobs:36 if !ok {37 return38 }39 result := process(ctx, job)40 select {41 case results <- result:42 case <-ctx.Done():43 return44 }45 }46 }47}48```4950## Testing:51- Run all concurrent tests with go test -race52- Use goleak package to detect goroutine leaks in tests53- Test with -count=100 to catch intermittent race conditionsCommon mistakes when fixing Concurrency Issues in Go Code from Cursor
Why it's a problem: Cursor generates unbuffered channels for async communication
How to avoid: Add to rules: ALWAYS use buffered channels when the sender should not block. Specify buffer sizes in prompts when requesting channel-based code.
Why it's a problem: Cursor launches goroutines without context cancellation
How to avoid: Add ALWAYS pass context.Context to goroutine functions and ALWAYS check ctx.Done() in goroutine loops.
Why it's a problem: Using time.Sleep for goroutine synchronization
How to avoid: Add to rules: NEVER use time.Sleep for synchronization. ALWAYS use sync.WaitGroup, errgroup, or channel receives.
Best practices
- Always run go test -race on Cursor-generated concurrent code before committing
- Include context.Context in every goroutine function signature in your rules
- Use errgroup.Group instead of bare WaitGroups when goroutines can fail
- Test with -count=100 to catch intermittent race conditions that appear rarely
- Use goleak in test teardown to detect goroutine leaks automatically
- Reference existing safe patterns in your project with @file when generating new concurrent code
- Start new Chat sessions for concurrent code generation to avoid context pollution from previous discussions
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Review this Go code for concurrency safety issues. Check for goroutine leaks, deadlock risks, race conditions on shared state, and missing context cancellation. For each issue found, show the fix and explain why the original code is unsafe.
@go-concurrency.mdc @pkg/worker/ Create a rate-limited worker pool with context cancellation, configurable concurrency, errgroup error propagation, and graceful shutdown. Every goroutine must have a clear termination path. No goroutine leaks allowed. Include tests that pass go test -race.
Frequently asked questions
Why does Cursor not use the Go race detector automatically?
The race detector is a runtime tool that requires running the code. Cursor generates code statically and cannot execute it. Always run go test -race yourself after generating concurrent code.
Should I use channels or mutexes for shared state?
Use channels when communicating between goroutines. Use mutexes when multiple goroutines need to read/write the same data structure. Add both patterns to your rules with guidance on when to use each.
How do I test for goroutine leaks?
Use the goleak package from Uber. Add goleak.VerifyNone(t) to your test functions. It detects goroutines that are still running when the test completes.
Can Cursor generate Go code with generics for concurrent patterns?
Yes, but specify Go 1.21+ in your rules and include a generic example. Cursor can generate generic worker pools, channels, and fan-out patterns when given type parameter examples.
What about using sync.Map vs regular map with mutex?
sync.Map is optimized for specific access patterns (mostly reads, or disjoint key sets). For general use, a regular map with sync.RWMutex is better. Specify your preference in your rules.
Can RapidDev help with Go concurrency architecture?
Yes. RapidDev designs concurrent Go systems with proper channel patterns, worker pools, and graceful shutdown, and configures Cursor rules to maintain safety standards across the team.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation