temporalio/temporal · error
unable to upsert custom search attributes: %w
Error message
unable to upsert custom search attributes: %w
What it means
The second statement in the ReplaceIntoVisibility transaction — the upsert into the custom search attributes table — failed, aborting the transaction. The error is wrapped as 'unable to upsert custom search attributes'. Common causes are custom search attribute constraint violations or stale schema on that table.
Source
Thrown at common/persistence/sql/sqlplugin/mysql/visibility.go:162
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)
}
}()
result, err = tx.NamedExecContext(ctx, templateUpsertWorkflowExecution, finalRow)
if err != nil {
return nil, fmt.Errorf("unable to upsert workflow execution: %w", err)
}
_, err = tx.NamedExecContext(ctx, templateUpsertCustomSearchAttributes, finalRow)
if err != nil {
return nil, fmt.Errorf("unable to upsert custom search attributes: %w", err)
}
_, err = tx.NamedExecContext(ctx, templateUpsertChasmSearchAttributes, finalRow)
if err != nil {
return nil, fmt.Errorf("unable to upsert chasm search attributes: %w", err)
}
err = tx.Commit()
if err != nil {
return nil, err
}
return result, nil
}
// DeleteFromVisibility deletes a row from visibility table if it exist
func (mdb *db) DeleteFromVisibility(
ctx context.Context,
filter sqlplugin.VisibilityDeleteFilter,
) (result sql.Result, retError error) {
defer func() {View on GitHub (pinned to bde624efd1)
Solutions
- Inspect the wrapped MySQL error for the precise constraint or truncation issue.
- Migrate the visibility schema so custom_search_attributes tables match the release.
- Validate custom search attribute keys/values against size and type limits.
- Align registered custom search attribute definitions with those set on workflows.
- Check DB connection health if the wrapped error is connection-level.
Example fix
// before: oversized custom SA value
sa := map[string]interface{}{"CustomKeywordField": strings.Repeat("x", 1000)}
// after: keep keyword fields within the column limit (e.g. <= 64 chars)
sa := map[string]interface{}{"CustomKeywordField": "x"} Defensive patterns
Strategy: validation
Validate before calling
// Go: validate SA value sizes before the upsert path
for k, v := range row.SearchAttributes {
if b, err := json.Marshal(v); err != nil || len(b) > maxSABlobSize {
return fmt.Errorf("search attribute %q exceeds size limit", k)
}
} Try / catch
if err := db.ReplaceIntoVisibility(ctx, row); err != nil {
if strings.Contains(err.Error(), "unable to upsert custom search attributes") {
logger.Error("custom SA upsert failed; check SA limits and schema", tag.Error(err))
return convertPersistenceErr(err)
}
return err
} Prevention
- Enforce search attribute key/value limits at the application boundary.
- Keep visibility schema migrated to the version matching the server.
- Ensure registered custom SA definitions match those used by workflows.
When it happens
Trigger: ReplaceIntoVisibility: templateUpsertCustomSearchAttributes NamedExecContext fails — SA row violates key constraints, attribute data too long, missing columns from an unmigrated schema, or connection failure.
Common situations: Emitting custom search attributes not defined/allowed for the SQL visibility store; schema migration skipped during upgrade; very large memo/attribute values exceeding column sizes.
Related errors
- unable to insert custom search attributes: %w
- unable to upsert workflow execution: %w
- unable to upsert chasm search attributes: %w
- requires a StartTime or CloseTime
- unable to insert workflow execution: %w
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/ed00892e6655d981.
Report an issue: GitHub.