{"record":{"id":"82ff607c41fd3576","repo":"temporalio/temporal","slug":"only-one-of-starttime-or-closetime-can-be-specifie","errorCode":null,"errorMessage":"only one of StartTime or CloseTime can be specified in a query","messagePattern":"only one of StartTime or CloseTime can be specified in a query","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/archiver/s3store/query_parser.go","lineNumber":72,"sourceCode":"\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}\n\n\tswitch expr := expr.(type) {\n\tcase *sqlparser.ComparisonExpr:","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/archiver/s3store/query_parser.go#L54-L90","documentation":"The s3store query parser supports time-range narrowing on exactly one of StartTime or CloseTime per query, because the archive time-window computation needs a single anchor. Parse rejects queries whose WHERE clause includes predicates on both time columns.","triggerScenarios":"Calling Parse with e.g. \"WorkflowId = 'w1' AND StartTime > '...' AND CloseTime < '...'\" — both parsedQuery.startTime and parsedQuery.closeTime are non-nil after convertWhereExpr.","commonSituations":"Users writing standard date-range queries (start AND end bounds) as they would against SQL databases; query builders that always add both start and close time bounds.","solutions":["Keep only one time predicate (StartTime or CloseTime) and rely on SearchPrecision for the scan window.","Apply the second time bound as post-filtering on returned records in application code.","Combine the remaining time condition with AND of SearchPrecision (required for time filters)."],"exampleFix":"// before\nparser.Parse(\"WorkflowId = 'w1' AND StartTime > '2024-01-01' AND CloseTime < '2024-02-01'\")\n// after\nparser.Parse(\"WorkflowId = 'w1' AND StartTime > '2024-01-01' AND SearchPrecision = 'Day'\")","handlingStrategy":"validation","validationCode":"hasStart := strings.Contains(query, \"StartTime\")\nhasClose := strings.Contains(query, \"CloseTime\")\nif hasStart && hasClose {\n    return errors.New(\"specify only one of StartTime or CloseTime\")\n}","typeGuard":null,"tryCatchPattern":"pq, err := parser.Parse(query)\nif err != nil {\n    if strings.Contains(err.Error(), \"only one of StartTime or CloseTime\") {\n        return nil, status.Error(codes.InvalidArgument, err.Error())\n    }\n    return nil, err\n}","preventionTips":["Build time ranges with a single archival time field plus SearchPrecision.","Apply the other time bound as an in-memory post-filter on results.","Reject date-range inputs at the UI layer for archive searches."],"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"}