temporalio/temporal · error · sadefs.ErrInvalidString

%w: %s

Error message

%w: %s

What it means

validateStrings wraps sadefs.ErrInvalidString when a decoded string search attribute value contains invalid UTF-8. The visibility store (Elasticsearch/SQL) cannot safely index or compare non-UTF-8 bytes, so decoding rejects them early with the offending value in the message.

Source

Thrown at common/searchattribute/sadefs/encode_value.go:81

	case enumspb.INDEXED_VALUE_TYPE_TEXT:
		return validateStrings(decodeValueTyped[string](value, allowList))
	case enumspb.INDEXED_VALUE_TYPE_KEYWORD_LIST:
		return validateStrings(decodeValueTyped[[]string](value, false))
	default:
		return nil, fmt.Errorf("%w: %v", ErrInvalidType, t)
	}
}

func validateStrings(anyValue any, err error) (any, error) {
	if err != nil {
		return anyValue, err
	}

	// validate strings
	switch value := anyValue.(type) {
	case string:
		if !utf8.ValidString(value) {
			return nil, fmt.Errorf("%w: %s", ErrInvalidString, value)
		}
	case []string:
		for _, item := range value {
			if !utf8.ValidString(item) {
				return nil, fmt.Errorf("%w: %s", ErrInvalidString, item)
			}
		}
	}
	return anyValue, err
}

// decodeValueTyped tries to decode to the given type.
// If the input is a list and allowList is false, then it will return only the first element.
// If the input is a list and allowList is true, then it will return the decoded list.
//
//nolint:revive // allowList is a control flag
func decodeValueTyped[T any](value *commonpb.Payload, allowList bool) (any, error) {
	// At first, it tries to decode to pointer of actual type (i.e. `*string` for `string`).

View on GitHub (pinned to bde624efd1)

Solutions

  1. Ensure the producer encodes strings as UTF-8 before attaching them as search attributes.
  2. Sanitize or transcode the offending value (the invalid string is included in the error message) at the workflow-start/memo layer.
  3. Use utf8.ValidString to pre-validate values before calling EncodeValue/DecodeValue.

Example fix

// before
payload, err := sadefs.EncodeValue(rawBytesAsString, false)
// after
if !utf8.ValidString(s) {
    s = strings.ToValidUTF8(s, "\uFFFD")
}
payload, err := sadefs.EncodeValue(s, false)
Defensive patterns

Strategy: validation

Validate before calling

if !utf8.ValidString(s) {
    return fmt.Errorf("search attribute value is not valid UTF-8")
}

Try / catch

if errors.Is(err, sadefs.ErrInvalidString) { /* sanitize and re-encode */ }

Prevention

When it happens

Trigger: DecodeValue on a Keyword, Text, or single-string payload whose bytes do not form valid UTF-8 — typically a payload written by a client that did not validate its string input.

Common situations: Binary data accidentally placed into a Keyword attribute; strings built from raw network bytes or wrong charset encodings (e.g. Latin-1) before being attached to a workflow.

Related errors


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