{"record":{"id":"7b12aa41e731f560","repo":"plandex-ai/plandex","slug":"error-starting-transaction-v","errorCode":null,"errorMessage":"error starting transaction: %v","messagePattern":"error starting transaction: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/locks.go","lineNumber":111,"sourceCode":"\tcancelFn := params.CancelFn\n\n\tif orgId == \"\" {\n\t\treturn \"\", fmt.Errorf(\"orgId is required\")\n\t}\n\tif planId == \"\" {\n\t\treturn \"\", fmt.Errorf(\"planId is required\")\n\t}\n\tif scope != LockScopeRead && scope != LockScopeWrite {\n\t\treturn \"\", fmt.Errorf(\"invalid lock scope: %s\", scope)\n\t}\n\n\ttx, err := Conn.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})\n\tif err != nil {\n\t\tif locksVerboseLogging {\n\t\t\tlog.Printf(\"[Lock][%d] Error starting transaction %v | reason: %s\",\n\t\t\t\tgoroutineID, err, params.Reason)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"error starting transaction: %v\", err)\n\t}\n\n\tvar committed bool\n\n\t// Ensure that rollback is attempted in case of failure\n\tdefer func() {\n\t\tif committed {\n\t\t\treturn\n\t\t}\n\n\t\tpanicErr := recover()\n\t\tif panicErr != nil {\n\t\t\tlog.Printf(\"panic in lock repo: %v\", panicErr)\n\t\t}\n\n\t\tif rbErr := tx.Rollback(); rbErr != nil {\n\t\t\tif rbErr == sql.ErrTxDone {\n\t\t\t\tif locksVerboseLogging {","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/locks.go#L93-L129","documentation":"lockRepoDB opens a REPEATABLE READ transaction with Conn.BeginTxx. If the driver cannot start the transaction (database unreachable, too many connections, context already cancelled/expired, pool exhausted), the error is wrapped as \"error starting transaction: %v\".","triggerScenarios":"Postgres down or restarting; connection pool exhausted (many concurrent lock attempts); params.Ctx cancelled or timed out mid-flight so BeginTxx aborts; bad DSN/credentials.","commonSituations":"Load spike causing \"too many clients already\"; network partition to the DB; long-running request whose context deadline expired before locking; misconfigured DATABASE_URL after a deploy.","solutions":["Read the wrapped driver error — \"too many connections\" means raise the pool size or reduce concurrency; \"context canceled/deadline exceeded\" means fix the caller's timeout.","Verify database connectivity and DSN (host, port, credentials, max_connections) with a simple ping.","Retry with backoff — the lock layer already retries; a persistent failure here indicates infra, not lock contention.","Ensure the context passed in LockRepoParams.Ctx outlives the lock attempt."],"exampleFix":"// before\ntx, err := Conn.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})\n// after (caller side)\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\nlockId, err := db.LockRepo(ctx, cancelFn, params) // retryable on transient errors","handlingStrategy":"retry","validationCode":"// before locking\nif err := db.Conn.PingContext(ctx); err != nil {\n    return fmt.Errorf(\"database unreachable: %w\", err)\n}\nif ctx.Err() != nil { return ctx.Err() }","typeGuard":"func ctxUsable(ctx context.Context) bool {\n    return ctx != nil && ctx.Err() == nil\n}","tryCatchPattern":"var lockId string\nvar err error\nfor i := 0; i < 3; i++ {\n    lockId, err = db.LockRepo(ctx, cancel, params)\n    if err == nil || !isTransientDBErr(err) { break }\n    time.Sleep(time.Duration(1<<i) * 200 * time.Millisecond)\n}","preventionTips":["Size the connection pool for peak lock concurrency (MaxOpenConns) and monitor pg_stat_activity.","Give every lock call a context timeout that comfortably exceeds total retry backoff.","Health-check DB connectivity at startup and alert on DSN/config regressions after deploys."],"tags":["database","transaction","connection","locking"],"backgroundTag":"database-connection-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}