temporalio/temporal · error
only one of WorkflowId or WorkflowType can be specified in a
Error message
only one of WorkflowId or WorkflowType can be specified in a query
What it means
The filestore/S3 visibility query parser allows exactly one primary key: either WorkflowId or WorkflowType, never both, since its archive lookup strategy can key off only one. Parse returns this error when the WHERE clause contains predicates on both columns.
Source
Thrown at common/archiver/s3store/query_parser.go:69
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.workflowID == nil && parsedQuery.workflowType == nil {
return nil, errors.New("WorkflowId or WorkflowType is required in query")
}
if parsedQuery.workflowID != nil && parsedQuery.workflowType != nil {
return nil, errors.New("only one of WorkflowId or WorkflowType can be specified in a query")
}
if parsedQuery.closeTime != nil && parsedQuery.startTime != nil {
return nil, errors.New("only one of StartTime or CloseTime can be specified in a query")
}
if (parsedQuery.closeTime != nil || parsedQuery.startTime != nil) && parsedQuery.searchPrecision == nil {
return nil, errors.New("SearchPrecision is required when searching for a StartTime or CloseTime")
}
if parsedQuery.closeTime == nil && parsedQuery.startTime == nil && parsedQuery.searchPrecision != nil {
return nil, errors.New("SearchPrecision requires 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")
}View on GitHub (pinned to bde624efd1)
Solutions
- Remove either the WorkflowId or the WorkflowType predicate, keeping the more selective one (WorkflowId).
- If both are needed for display, filter by WorkflowId and verify WorkflowType after fetching records.
- Validate query input at the UI/API layer to reject both fields being set before invoking Parse.
Example fix
// before
parser.Parse("WorkflowId = 'w1' AND WorkflowType = 'MyType'")
// after
parser.Parse("WorkflowId = 'w1'") Defensive patterns
Strategy: validation
Validate before calling
var wfID, wfType bool
for _, f := range filters {
if f.Name == "WorkflowId" { wfID = true }
if f.Name == "WorkflowType" || f.Name == "WorkflowTypeName" { wfType = true }
}
if wfID && wfType {
return errors.New("specify only one of WorkflowId or WorkflowType")
} Try / catch
pq, err := parser.Parse(query)
if err != nil {
if strings.Contains(err.Error(), "only one of WorkflowId or WorkflowType") {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return nil, err
} Prevention
- In query builders, prefer WorkflowId and drop WorkflowType when both are supplied.
- Validate mutual exclusivity at the API/UI layer before invoking Parse.
- Educate users that archival queries are not full SQL.
When it happens
Trigger: Calling Parse with a WHERE clause like "WorkflowId = 'w1' AND WorkflowType = 'MyType'", including via AND-chained or parenthesized comparisons handled by convertAndExpr/convertParenExpr.
Common situations: Users porting queries from standard SQL visibility (which permits combined filters); query builders that append both user-supplied workflow ID and type unconditionally.
Related errors
- WorkflowId or WorkflowType is required in query
- only one of StartTime or CloseTime can be specified in a que
- SearchPrecision is required when searching for a StartTime o
- SearchPrecision requires a StartTime or CloseTime
- where expression is nil
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/1007b36cccf6efb0.
Report an issue: GitHub.