temporalio/temporal · error

requires a StartTime or CloseTime

Error message

requires a StartTime or CloseTime

What it means

GetMapper builds a search-attribute mapper for the current index and, when a fallback (secondary/legacy) index is configured, also loads that index's search attributes via searchAttributesProvider.GetSearchAttributes. If the fallback index's attributes cannot be loaded (e.g. the index does not exist in Elasticsearch or the persistence call fails), this wrapping error is returned with the index name and cause.

Source

Thrown at common/archiver/gcloud/query_parser.go:68

// NewQueryParser creates a new query parser for filestore
func NewQueryParser() QueryParser {
	return &queryParser{}
}

func (p *queryParser) Parse(query string) (*parsedQuery, error) {
	stmt, err := sqlparser.Parse(fmt.Sprintf(sqlquery.QueryTemplate, query))
	if err != nil {
		return nil, err
	}
	whereExpr := stmt.(*sqlparser.Select).Where.Expr
	parsedQuery := &parsedQuery{}
	if err := p.convertWhereExpr(whereExpr, parsedQuery); err != nil {
		return nil, err
	}

	if (parsedQuery.closeTime.IsZero() && parsedQuery.startTime.IsZero()) || (!parsedQuery.closeTime.IsZero() && !parsedQuery.startTime.IsZero()) {
		return nil, errors.New("requires a StartTime or CloseTime")
	}

	if parsedQuery.searchPrecision == nil {
		return nil, errors.New("SearchPrecision is required when searching for a StartTime or CloseTime")
	}

	return parsedQuery, nil
}

func (p *queryParser) convertWhereExpr(expr sqlparser.Expr, parsedQuery *parsedQuery) error {
	if expr == nil {
		return errors.New("where expression is nil")
	}

	switch expr := expr.(type) {
	case *sqlparser.ComparisonExpr:
		return p.convertComparisonExpr(expr, parsedQuery)
	case *sqlparser.AndExpr:

View on GitHub (pinned to bde624efd1)

Solutions

  1. Verify the fallback index exists in Elasticsearch (GET /_cat/indices) and create it if missing.
  2. Correct the fallbackIndexName in your visibility config if it is a typo.
  3. Check ES connectivity and credentials of the visibility store client.
  4. If no fallback is needed, clear fallbackIndexName so the fallback path is skipped.

Example fix

// before (config)
visibilityConfig:
  fallbackIndexName: "temporal-visibility-v1"  // deleted index
// after
visibilityConfig:
  fallbackIndexName: "temporal-secondary-v2"  // existing index, or remove field
Defensive patterns

Strategy: fallback

Validate before calling

// Before enabling a fallback index, confirm it exists
exists, err := esClient.Indices.Exists([]string{fallbackIndexName}).Do(ctx)
if err != nil || !exists {
	return fmt.Errorf("fallback index %q does not exist", fallbackIndexName)
}

Try / catch

mapper, err := provider.GetMapper(ctx)
if err != nil && strings.Contains(err.Error(), "failed to load search attributes for fallback index") {
	// disable fallback path or alert, then retry/continue with primary index only
}

Prevention

When it happens

Trigger: Calling GetMapper when m.fallbackIndexName != "" and searchAttributesProvider.GetSearchAttributes(fallbackIndexName, false) returns an error — most often because the fallback index is missing from Elasticsearch, or the ES request fails (auth, connectivity, mapping unavailable).

Common situations: Config references a fallback/secondary visibility index that was never created or was deleted; index name typo; Elasticsearch credentials/permissions changed after an upgrade; during version upgrades where the old index was removed before config was updated.

Related errors


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