temporalio/temporal · error

unable to insert custom search attributes: %w

Error message

unable to insert custom search attributes: %w

What it means

The second statement of the InsertIntoVisibility transaction — inserting into the custom search attributes table (templateInsertCustomSearchAttributes) — failed. Because this happens inside a transaction, everything is rolled back and the error is wrapped as 'unable to insert custom search attributes'. This usually means schema drift on the custom search attributes table or invalid/oversized search attribute values.

Source

Thrown at common/persistence/sql/sqlplugin/mysql/visibility.go:118

	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, templateInsertWorkflowExecution, finalRow)
	if err != nil {
		return nil, fmt.Errorf("unable to insert workflow execution: %w", err)
	}
	_, err = tx.NamedExecContext(ctx, templateInsertCustomSearchAttributes, finalRow)
	if err != nil {
		return nil, fmt.Errorf("unable to insert custom search attributes: %w", err)
	}
	_, err = tx.NamedExecContext(ctx, templateInsertChasmSearchAttributes, finalRow)
	if err != nil {
		return nil, fmt.Errorf("unable to insert chasm search attributes: %w", err)
	}
	err = tx.Commit()
	if err != nil {
		return nil, err
	}
	return result, nil
}

// ReplaceIntoVisibility replaces an existing row if it exist or creates a new row in visibility table
func (mdb *db) ReplaceIntoVisibility(
	ctx context.Context,
	row *sqlplugin.VisibilityRow,
) (result sql.Result, retError error) {
	defer func() {

View on GitHub (pinned to bde624efd1)

Solutions

  1. Inspect the wrapped MySQL error to see the specific constraint or data issue.
  2. Apply visibility schema migrations so the custom search attributes table matches expectations.
  3. Validate search attribute keys/values conform to limits (key length, allowed types) before starting the workflow.
  4. Confirm registered custom search attribute definitions match what the workflow emits.
  5. Check DB connectivity if the wrapped error is a connection/driver error.

Example fix

// before
workflowOpts.SearchAttributes = map[string]interface{}{
    "CustomIntField & Partner": 42, // invalid key characters
}

// after
workflowOpts.SearchAttributes = map[string]interface{}{
    "CustomIntFieldPartner": 42,
}
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate SA keys before starting the workflow
for key := range searchAttributes {
    if len(key) > 64 || strings.ContainsAny(key, "& %$#@") {
        return fmt.Errorf("invalid custom search attribute key: %q", key)
    }
}

Try / catch

if err := db.InsertIntoVisibility(ctx, row); err != nil {
    if strings.Contains(err.Error(), "unable to insert custom search attributes") {
        logger.Error("custom SA insert failed; validate SA keys and schema", tag.Error(err))
        return convertPersistenceErr(err)
    }
    return err
}

Prevention

When it happens

Trigger: InsertIntoVisibility: templateInsertCustomSearchAttributes NamedExecContext returns an error — custom search attributes row violates constraints, custom search attribute keys exceed limits, or the table schema is out of date.

Common situations: Using custom SearchAttributes not supported by the SQL visibility store version; SA key names too long or with unsupported characters; visibility schema not migrated; timezone/datetime encoding issues in attribute values.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/d856ab096b4132fd. Report an issue: GitHub.