googleapis/mcp-toolbox · error

unable to execute DML statement: %w

Error message

unable to execute DML statement: %w

What it means

Returned by Source.RunSQL when a non-readOnly statement is executed with ExecContext and the Oracle driver reports an execution error. The statement reached the database (or attempted to) but failed to execute — SQL syntax errors, ORA-xxxxx errors, permission errors, or connection drops are wrapped here. It applies to INSERT/UPDATE/DELETE/DDL/PL-SQL executed through the execute-sql tool path.

Source

Thrown at internal/sources/oracle/oracle.go:149

}

func (s *Source) SourceType() string {
	return SourceType
}

func (s *Source) ToConfig() sources.SourceConfig {
	return s.Config
}

func (s *Source) OracleDB() *sql.DB {
	return s.DB
}

func (s *Source) RunSQL(ctx context.Context, statement string, params []any, readOnly bool) (any, error) {
	if !readOnly {
		result, err := s.OracleDB().ExecContext(ctx, statement, params...)
		if err != nil {
			return nil, fmt.Errorf("unable to execute DML statement: %w", err)
		}

		rowsAffected, err := result.RowsAffected()
		if err != nil {
			return nil, fmt.Errorf("unable to get rows affected: %w", err)
		}

		return map[string]any{
			"status":        "success",
			"rows_affected": rowsAffected,
		}, nil
	}
	rows, err := s.OracleDB().QueryContext(ctx, statement, params...)
	if err != nil {
		return nil, fmt.Errorf("unable to execute query: %w", err)
	}
	defer rows.Close()

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Inspect the wrapped ORA-xxxxx code; look up its meaning (e.g. ORA-00911 → remove trailing semicolon or invalid characters).
  2. Run the exact statement in SQL Developer/sqlplus as the same user to confirm it is valid SQL, not a driver issue.
  3. Verify the connected user has the required object privileges (SELECT/INSERT/etc.) and the object is schema-qualified if needed.
  4. Check that the number of ? placeholders matches len(params).
  5. If it was a transient network error, verify connectivity and retry the statement.

Example fix

// before (LLM sent trailing semicolon → ORA-00911)
statement: "UPDATE employees SET salary = salary * 1.1;"
// after
statement: "UPDATE employees SET salary = salary * 1.1"
Defensive patterns

Strategy: try-catch

Validate before calling

// strip trailing semicolons and empty statements before invoking RunSQL
stmt := strings.TrimSpace(statement)
stmt = strings.TrimSuffix(stmt, ";")
if stmt == "" {
	return fmt.Errorf("empty statement")
}

Type guard

func isDMLExecError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "unable to execute DML statement")
}

Try / catch

result, err := src.RunSQL(ctx, stmt, params, false)
if err != nil {
	if isDMLExecError(err) {
		var oraCode string
		if m := regexp.MustCompile(`ORA-\d{5}`).FindStringSubmatch(err.Error()); m != nil {
			oraCode = m[0]
		}
		log.Printf("DML failed (%s): %v", oraCode, err)
		return fmt.Errorf("statement rejected by oracle (%s): %w", oraCode, err)
	}
	return err
}

Prevention

When it happens

Trigger: RunSQL(ctx, statement, params, readOnly=false) calling s.OracleDB().ExecContext where the driver returns an error: ORA-00942 table or view does not exist, ORA-00911 invalid character (e.g. trailing semicolon), ORA-01013 user requested cancel, ORA-01031 insufficient privileges, or parameter count/type mismatch.

Common situations: LLM-generated SQL containing a trailing semicolon (ORA-00911); querying a table without schema qualification or grants; referencing a dropped/renamed table; sending a parameterized statement with wrong number of placeholders; session killed by DBA or network interruption mid-execution.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/1edefc7401020ea0. Report an issue: GitHub.