{"record":{"id":"1007b36cccf6efb0","repo":"temporalio/temporal","slug":"only-one-of-workflowid-or-workflowtype-can-be-spec","errorCode":null,"errorMessage":"only one of WorkflowId or WorkflowType can be specified in a query","messagePattern":"only one of WorkflowId or WorkflowType can be specified in a query","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/archiver/s3store/query_parser.go","lineNumber":69,"sourceCode":"func NewQueryParser() QueryParser {\n\treturn &queryParser{}\n}\n\nfunc (p *queryParser) Parse(query string) (*parsedQuery, error) {\n\tstmt, err := sqlparser.Parse(fmt.Sprintf(sqlquery.QueryTemplate, query))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\twhereExpr := stmt.(*sqlparser.Select).Where.Expr\n\tparsedQuery := &parsedQuery{}\n\tif err := p.convertWhereExpr(whereExpr, parsedQuery); err != nil {\n\t\treturn nil, err\n\t}\n\tif parsedQuery.workflowID == nil && parsedQuery.workflowType == nil {\n\t\treturn nil, errors.New(\"WorkflowId or WorkflowType is required in query\")\n\t}\n\tif parsedQuery.workflowID != nil && parsedQuery.workflowType != nil {\n\t\treturn nil, errors.New(\"only one of WorkflowId or WorkflowType can be specified in a query\")\n\t}\n\tif parsedQuery.closeTime != nil && parsedQuery.startTime != nil {\n\t\treturn nil, errors.New(\"only one of StartTime or CloseTime can be specified in a query\")\n\t}\n\tif (parsedQuery.closeTime != nil || parsedQuery.startTime != nil) && parsedQuery.searchPrecision == nil {\n\t\treturn nil, errors.New(\"SearchPrecision is required when searching for a StartTime or CloseTime\")\n\t}\n\n\tif parsedQuery.closeTime == nil && parsedQuery.startTime == nil && parsedQuery.searchPrecision != nil {\n\t\treturn nil, errors.New(\"SearchPrecision requires a StartTime or CloseTime\")\n\t}\n\treturn parsedQuery, nil\n}\n\nfunc (p *queryParser) convertWhereExpr(expr sqlparser.Expr, parsedQuery *parsedQuery) error {\n\tif expr == nil {\n\t\treturn errors.New(\"where expression is nil\")\n\t}","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/archiver/s3store/query_parser.go#L51-L87","documentation":"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.","triggerScenarios":"Calling Parse with a WHERE clause like \"WorkflowId = 'w1' AND WorkflowType = 'MyType'\", including via AND-chained or parenthesized comparisons handled by convertAndExpr/convertParenExpr.","commonSituations":"Users porting queries from standard SQL visibility (which permits combined filters); query builders that append both user-supplied workflow ID and type unconditionally.","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."],"exampleFix":"// before\nparser.Parse(\"WorkflowId = 'w1' AND WorkflowType = 'MyType'\")\n// after\nparser.Parse(\"WorkflowId = 'w1'\")","handlingStrategy":"validation","validationCode":"var wfID, wfType bool\nfor _, f := range filters {\n    if f.Name == \"WorkflowId\" { wfID = true }\n    if f.Name == \"WorkflowType\" || f.Name == \"WorkflowTypeName\" { wfType = true }\n}\nif wfID && wfType {\n    return errors.New(\"specify only one of WorkflowId or WorkflowType\")\n}","typeGuard":null,"tryCatchPattern":"pq, err := parser.Parse(query)\nif err != nil {\n    if strings.Contains(err.Error(), \"only one of WorkflowId or WorkflowType\") {\n        return nil, status.Error(codes.InvalidArgument, err.Error())\n    }\n    return nil, err\n}","preventionTips":["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."],"tags":["go","archival","query-validation","s3store"],"backgroundTag":"mutually-exclusive-query-filters","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}