temporalio/temporal · error
unable to delete custom search attributes: %w
Error message
unable to delete custom search attributes: %w
What it means
Returned when the DELETE against templateDeleteCustomSearchAttributes fails inside the DeleteFromVisibility transaction. The transaction is rolled back by the deferred handler, so no partial deletion occurs. The wrapped driver error carries the real cause (constraint violation, connection failure, syntax error, etc.).
Source
Thrown at common/persistence/sql/sqlplugin/mysql/visibility.go:202
if err != nil {
return nil, err
}
tx, err := db.BeginTxx(ctx, nil)
if err != nil {
return nil, err
}
defer func() {
err := tx.Rollback()
// If the error is sql.ErrTxDone, it means the transaction already closed, so ignore error.
if err != nil && !errors.Is(err, sql.ErrTxDone) {
// Transaction rollback error should never happen, unless db connection was lost.
retError = fmt.Errorf("transaction rollback failed: %w", retError)
}
}()
_, err = tx.NamedExecContext(ctx, templateDeleteCustomSearchAttributes, filter)
if err != nil {
return nil, fmt.Errorf("unable to delete custom search attributes: %w", err)
}
result, err = tx.NamedExecContext(ctx, templateDeleteWorkflowExecution_v8, filter)
if err != nil {
return nil, fmt.Errorf("unable to delete workflow execution: %w", err)
}
_, err = tx.NamedExecContext(ctx, templateDeleteChasmSearchAttributes, filter)
if err != nil {
return nil, fmt.Errorf("unable to delete chasm search attributes: %w", err)
}
err = tx.Commit()
if err != nil {
return nil, err
}
return result, nil
}
// SelectFromVisibility reads one or more rows from visibility table
func (mdb *db) SelectFromVisibility(View on GitHub (pinned to bde624efd1)
Solutions
- Inspect the wrapped cause with errors.Unwrap / %v of the error to see the MySQL errno.
- Run the latest visibility schema migration so all tables (including CHASM search attributes) exist.
- Retry the delete; transient lock timeouts and deadlocks resolve on retry.
- Check for concurrent deletes of the same WorkflowExecution (RunID) causing lock contention.
Defensive patterns
Strategy: retry
Validate before calling
// Go: ensure schema is current before operations
var exists int
err := db.QueryRow("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ?", schemaName, "custom_search_attributes").Scan(&exists)
if err != nil || exists == 0 { /* run migrations */ } Try / catch
// Go
err := store.DeleteFromVisibility(ctx, req)
if err != nil {
var retryable bool
if isDeadlockOrTimeout(err) { retryable = true }
return wrapAndMaybeRetry(err, retryable)
} Prevention
- Keep visibility schema migrations applied before deploying new server versions
- Use retry policies for deadlock/lock-timeout MySQL errors (1205, 1213)
- Avoid concurrent deletes of the same RunID
- Grant the DB user full DML privileges on all visibility tables
When it happens
Trigger: Calling DeleteFromVisibility (public API of the MySQL visibility store) when the named exec on the custom search attributes table errors — missing table/schema mismatch, connection loss, or lock wait timeout on the executions_visibility row.
Common situations: Visibility schema not migrated (custom search attributes table missing after upgrade); deadlocks/lock timeouts from concurrent deletes; DB failover mid-transaction.
Understand the failure class
Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.
Related errors
- unable to delete workflow execution: %w
- unable to delete chasm search attributes: %w
- transaction rollback failed: %w
- rowsAffected returned error when initializing DLQ metadata
- ErrQueueAlreadyExists
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/6a1f6b91a1457395.
Report an issue: GitHub.