temporalio/temporal · error
unable to upsert custom search attributes
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.
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
- unable to upsert chasm search attributes
- unable to upsert workflow execution
- requires a StartTime or CloseTime
- unable to insert chasm search attributes
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/ed00892e6655d981.
Report an issue: GitHub.
Appendix: 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)