SigNoz/signoz · error

internal

internal

Error message

cannot begin transaction

What it means

RunInTxCtx is the generic transactional helper: when already inside a tx it just invokes the callback, otherwise it begins a new one with BeginTx. This error means a new transaction could not be started — connection acquisition failed, the context is invalid/canceled, or the DB is unavailable/read-only.

Source

Thrown at pkg/sqlstore/bun.go:40

	db := bun.NewDB(sqldb, dialect, opts...)

	for _, hook := range hooks {
		db.AddQueryHook(hook)
	}

	return &BunDB{db, settings}
}

func (db *BunDB) RunInTxCtx(ctx context.Context, opts *sql.TxOptions, cb func(ctx context.Context) error) error {
	tx, ok := txFromContext(ctx)
	if ok {
		return cb(ctx)
	}

	// begin transaction
	tx, err := db.BeginTx(ctx, opts)
	if err != nil {
		return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "cannot begin transaction")
	}

	defer func() {
		if err := tx.Rollback(); err != nil {
			if err != sql.ErrTxDone {
				db.settings.Logger().ErrorContext(ctx, "cannot rollback transaction", errors.Attr(err))
			}
		}
	}()

	if err := cb(newContextWithTx(ctx, tx)); err != nil {
		return err
	}

	return tx.Commit()
}

func (db *BunDB) BunDBCtx(ctx context.Context) bun.IDB {

View on GitHub (pinned to 5069bf80b0)

Solutions

  1. Inspect wrapped error: context.Canceled vs too many connections vs bad conn
  2. Tune pool (max open conns, max idle, conn max lifetime) and check for tx leaks
  3. Retry with backoff for transient unavailability
  4. Point the service at a writable primary, not a read replica

Example fix

// before
err := sqlstore.RunInTxCtx(ctx, func(ctx context.Context) error { ... })

// after
err := retry.Do(func() error {
    return sqlstore.RunInTxCtx(ctx, func(ctx context.Context) error { ... })
}, retry.Attempts(3), retry.RetryIf(isTransientConnErr))
Defensive patterns

Strategy: retry

Validate before calling

if ctx.Err() != nil { return ctx.Err() }
if err := db.PingContext(ctx); err != nil { return err }

Type guard

func isBeginTxFailure(err error) bool {
    return err != nil && strings.Contains(err.Error(), "cannot begin transaction")
}

Try / catch

err := sqlstore.RunInTxCtx(ctx, cb)
if isBeginTxFailure(err) && isTransientDBErr(errors.Unwrap(err)) {
    time.Sleep(250*time.Millisecond)
    err = sqlstore.RunInTxCtx(ctx, cb)
}

Prevention

When it happens

Trigger: Calling RunInTxCtx (not nested) when the pool has no available connections, ctx is canceled, the DB is restarting, or max connection limits are hit server-side.

Common situations: Pool saturation from leaked/long transactions, Postgres max_connections exceeded, failover in progress, canceled parent contexts (HTTP client disconnects), read-only replicas targeted by mistake.

Related errors


AI-assisted analysis of SigNoz/signoz@5069bf80b0 (2026-08-28). Data as JSON: /api/errors/a3d9855ab03d62e9. Report an issue: GitHub.