{"record":{"id":"b2e65f421ffb538e","repo":"gastownhall/beads","slug":"failed-to-read-active-branch-w","errorCode":null,"errorMessage":"failed to read active branch: %w","messagePattern":"failed to read active branch: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/transaction.go","lineNumber":191,"sourceCode":"\t// Detect pool-wait: if WaitCount increased, the pool was exhausted and\n\t// this caller had to wait for a connection to become available.\n\tif err == nil {\n\t\tstatsAfter := s.db.Stats()\n\t\tif statsAfter.WaitCount > statsBefore.WaitCount {\n\t\t\tdoltMetrics.poolWaitCount.Add(ctx, statsAfter.WaitCount-statsBefore.WaitCount)\n\t\t\twaitMs := float64(statsAfter.WaitDuration-statsBefore.WaitDuration) / float64(time.Millisecond)\n\t\t\tdoltMetrics.poolWaitMs.Record(ctx, waitMs)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to acquire connection: %w\", err)\n\t}\n\tdefer conn.Close()\n\n\tvar currentBranch string\n\tif err := conn.QueryRowContext(ctx, \"SELECT active_branch()\").Scan(&currentBranch); err != nil {\n\t\treturn fmt.Errorf(\"failed to read active branch: %w\", err)\n\t}\n\n\tregularTx, err := conn.BeginTx(ctx, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to begin regular tx: %w\", err)\n\t}\n\n\t// The journal counter and rows must commit in the SAME SQL transaction as\n\t// every mutation they describe. bd_events_journal and bd_events_seq are\n\t// dolt_ignored, so on the default split-transaction shape they would land in\n\t// the ignored transaction while the mutation lands in the regular one: a\n\t// mixed durable+wisp callback would then make the two transactions contend\n\t// with each other on the single bd_events_seq row, and the ignored commit\n\t// can fail AFTER the regular side has already committed — a mutation with no\n\t// journal record, which is exactly the state the same-transaction guarantee\n\t// exists to make impossible. In journal mode both planes therefore share the\n\t// pinned regular transaction. The default journal-off path keeps the\n\t// established split transactions untouched.","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/transaction.go#L173-L209","documentation":"After acquiring a pooled connection, runDoltTransaction runs `SELECT active_branch()` to learn which Dolt branch the session is on, so the ignored-tables transaction can be pinned to the same branch. This error wraps a failure of that query — the connection is unusable (dead/stale server session), the query was canceled by ctx, or the server is not speaking the Dolt SQL dialect expected here.","triggerScenarios":"ctx canceled or deadline exceeded during the round-trip; the pooled connection went stale (server restarted, idle timeout, wait_timeout); connecting to a plain MySQL server instead of a Dolt SQL server where active_branch() does not exist.","commonSituations":"Dolt server restart between pool warm-up and next use; aggressive proxy/firewall killing idle sessions; accidentally pointing beads at a MySQL-only DSN; extremely short context timeouts.","solutions":["Verify the endpoint is a Dolt SQL server, not plain MySQL (active_branch() is Dolt-specific)","Retry the operation — setup-phase failures before the callback are retried automatically by withTransactionSetupRetry","Check ctx deadlines and network stability between client and Dolt server","Ping/health-check the pool before writes so stale sessions are discarded"],"exampleFix":"// before\nctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n// after\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // allow branch-read round-trip","handlingStrategy":"retry","validationCode":"if err := db.PingContext(ctx); err != nil { return fmt.Errorf(\"stale pool: %w\", err) }","typeGuard":null,"tryCatchPattern":"if err != nil && errors.Is(err, context.DeadlineExceeded) {\n    // pre-callback setup failure: retried automatically; or retry once manually\n    return retrySetup(ctx)\n}","preventionTips":["Point the DSN at a real Dolt SQL server, never plain MySQL","Set ConnMaxLifetime below server idle timeout to avoid stale sessions","Avoid sub-second context timeouts around transaction setup","Monitor server restarts; recycle the pool after one"],"tags":["go","dolt","sql","session"],"backgroundTag":"dolt-active-branch-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}