t8y2/dbx · error

cannot start a one-shot transaction while a manual…

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.

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.

Appendix: 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)