t8y2/dbx · error

cannot start a one-shot transaction while a manual transacti

Error message

cannot start a one-shot transaction while a manual transaction is open

What it means

executeTransaction runs one-shot, multi-statement transactions and is mutually exclusive with the agent's manual transaction mode. If a manual transaction is currently open (hasManualTransaction() is true), starting a one-shot transaction would mix two independent transaction contexts on the same session, so the agent rejects the request outright.

Source

Thrown at agents/drivers/oracle-go/main.go:3704

	if !isOracleIdentifierStart(next) {
		return oracleBindParam{}, pos, false
	}
	end := pos + 2
	for end < len(sqlText) && isOracleIdentifierPart(sqlText[end]) {
		end++
	}
	return oracleBindParam{Name: sqlText[pos+1 : end]}, end, true
}

func restoreOracleCurrentSchema(conn *sql.Conn, schema string) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	_, _ = conn.ExecContext(ctx, "ALTER SESSION SET CURRENT_SCHEMA = "+quoteIdentifier(schema))
}

func (s *server) executeTransaction(params map[string]json.RawMessage) (queryResult, error) {
	if s.hasManualTransaction() {
		return queryResult{}, errors.New("cannot start a one-shot transaction while a manual transaction is open")
	}
	var payload struct {
		Statements []string `json:"statements"`
		Schema     string   `json:"schema"`
	}
	if err := decodeParams(params, &payload); err != nil {
		return queryResult{}, err
	}
	db, err := s.requireDB()
	if err != nil {
		return queryResult{}, err
	}
	tx, err := db.Begin()
	if err != nil {
		return queryResult{}, err
	}
	if strings.TrimSpace(payload.Schema) != "" {
		if _, err := tx.Exec("ALTER SESSION SET CURRENT_SCHEMA = " + quoteIdentifier(payload.Schema)); err != nil {

View on GitHub (pinned to c0390bff16)

Solutions

  1. Commit or roll back the open manual transaction before calling executeTransaction
  2. Route one-shot transactions through a different agent/connection than the one holding the manual transaction
  3. Audit error paths so manual transactions are always ended (deferred commit/rollback)
  4. Check hasManualTransaction() before issuing executeTransaction and branch accordingly

Example fix

// before
await driver.executeTransaction({ statements: ["INSERT ..."] }) // errors while manual tx open
// after
if (await driver.hasManualTransaction()) {
  await driver.commitTransaction()
}
await driver.executeTransaction({ statements: ["INSERT ..."] })
Defensive patterns

Strategy: validation

Validate before calling

if (driver.hasManualTransaction()) {
  throw new Error('end the manual transaction (commit/rollback) before running a one-shot transaction')
}

Type guard

function canRunOneShotTx(d) {
  return typeof d.hasManualTransaction === 'function' && !d.hasManualTransaction()
}

Try / catch

try {
  await driver.executeTransaction({ statements })
} catch (e) {
  if (String(e.message).includes('manual transaction is open')) {
    await driver.rollbackTransaction()
    return driver.executeTransaction({ statements }) // retry once
  }
  throw e
}

Prevention

When it happens

Trigger: Calling the execute-transaction RPC while a 'begin manual transaction' is open (no commit/rollback yet); a background job issuing one-shot transactions while a long-lived manual transaction is held; leaked manual transactions that were never committed or rolled back.

Common situations: An app holding a manual transaction for interactive edits while scheduled jobs fire one-shot transactions through the same agent; an exception path that left a manual transaction open, blocking all subsequent executeTransaction calls; connection-pool sharing across components with different transaction styles.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/f826ffbd7f249c8a. Report an issue: GitHub.