gastownhall/beads · error
failed to read issue_id_mode config: %w
Error message
failed to read issue_id_mode config: %w
What it means
IsCounterModeTx reads the issue_id_mode row from the config table to decide whether issue IDs are generated by counter or by hash probing. This error means that SELECT failed with something other than 'no rows' — i.e., a genuine query failure, not an unset config. A missing config row is explicitly tolerated and treated as non-counter (hash) mode.
Source
Thrown at internal/storage/issueops/helpers.go:224
if err != nil {
return "", fmt.Errorf("failed to check for ID collision: %w", err)
}
if count == 0 {
return candidate, nil
}
}
}
return "", fmt.Errorf("failed to generate unique ID after trying lengths %d-%d with 10 nonces each", baseLength, maxLength)
}
// IsCounterModeTx checks whether issue_id_mode=counter is configured.
func IsCounterModeTx(ctx context.Context, tx DBTX) (bool, error) {
var idMode string
err := tx.QueryRowContext(ctx, "SELECT value FROM config WHERE `key` = ?", "issue_id_mode").Scan(&idMode)
if err != nil && err != sql.ErrNoRows {
return false, fmt.Errorf("failed to read issue_id_mode config: %w", err)
}
return idMode == "counter", nil
}
// NextCounterIDTx atomically increments and returns the next sequential issue ID.
func NextCounterIDTx(ctx context.Context, tx DBTX, prefix string) (string, error) {
res, err := tx.ExecContext(ctx, "UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?", prefix)
if err != nil {
return "", fmt.Errorf("failed to increment issue counter for prefix %q: %w", prefix, err)
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return "", fmt.Errorf("failed to check rows affected for issue counter prefix %q: %w", prefix, err)
}
if rowsAffected == 0 {
if seedErr := SeedCounterFromExistingIssuesTx(ctx, tx, prefix); seedErr != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped driver error to identify the root cause (missing table vs lock vs connection)
- Create/verify the config table exists with the expected key column: SELECT value FROM config WHERE key = 'issue_id_mode'
- Set the mode explicitly if desired: insert issue_id_mode = 'counter' (or 'hash') into config
- Run schema migrations/bd doctor to repair a missing or corrupted config table
Example fix
// before
err = tx.QueryRowContext(ctx, "SELECT value FROM config WHERE `key` = ?", "issue_id_mode").Scan(&idMode)
if err != nil && err != sql.ErrNoRows {
return false, fmt.Errorf("failed to read issue_id_mode config: %w", err)
}
// after
err = tx.QueryRowContext(ctx, "SELECT value FROM config WHERE `key` = ?", "issue_id_mode").Scan(&idMode)
if errors.Is(err, sql.ErrNoRows) {
return false, nil // unset config: default hash mode
}
if err != nil {
return false, fmt.Errorf("failed to read issue_id_mode config: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify config table exists before generation
var n int
if err := tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM sqlite_master WHERE name = 'config'`).Scan(&n); err == nil && n == 0 {
return errors.New("config table missing: run bd init/migrations")
} Try / catch
isCounter, err := IsCounterModeTx(ctx, tx)
if err != nil {
if strings.Contains(err.Error(), "no such table") {
// treat as hash mode default or run migrations first
isCounter = false
} else {
return fmt.Errorf("id mode lookup: %w", err)
}
} Prevention
- Initialize the config table during database setup, before any issue creation
- Set issue_id_mode explicitly instead of relying on the no-rows default
- Run migrations/bd doctor after version upgrades
- Avoid concurrent writers contending for the config table during ID generation
When it happens
Trigger: Calling GenerateIssueIDInTable (which calls IsCounterModeTx) when the config table is missing/corrupted, the query hits a lock timeout, the transaction is dead, or the connection fails mid-query. Note: sql.ErrNoRows is deliberately NOT an error path here.
Common situations: Fresh database where the config table was never created (pre-migration state); database locked by a concurrent writer; corrupted config table after a crashed migration.
Related errors
- delete config %s: %w
- failed to check for ID collision: %w
- db: NextCounterID: prefix must not be empty
- failed to load config: %w
- failed to begin transaction: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f39ffcced59aa436.
Report an issue: GitHub.