temporalio/temporal · error

missing or invalid query

Error message

missing or invalid query

What it means

handleListMatchingTimesQuery handles the ListScheduleMatchingTimes workflow query. It requires a non-nil request carrying both StartTime and EndTime; if the query input is missing or lacks the time range, it returns this error. A nil s.cspec additionally indicates the schedule itself is invalid and yields a different error.

Source

Thrown at service/worker/scheduler/workflow.go:1167

	return out
}

func (s *scheduler) handleDescribeQuery() (*schedulespb.DescribeResponse, error) {
	// this is a query handler, don't modify s.Info directly
	infoCopy := common.CloneProto(s.Info)
	infoCopy.FutureActionTimes = s.getFutureActionTimes(false, s.tweakables.FutureActionCount)
	infoCopy.BufferSize = int64(len(s.State.BufferedStarts))

	return &schedulespb.DescribeResponse{
		Schedule:      s.Schedule,
		Info:          infoCopy,
		ConflictToken: s.State.ConflictToken,
	}, nil
}

func (s *scheduler) handleListMatchingTimesQuery(req *workflowservice.ListScheduleMatchingTimesRequest) (*workflowservice.ListScheduleMatchingTimesResponse, error) {
	if req == nil || req.StartTime == nil || req.EndTime == nil {
		return nil, errors.New("missing or invalid query")
	}
	if s.cspec == nil {
		return nil, fmt.Errorf("invalid schedule: %s", s.Info.InvalidScheduleError)
	}

	var out []*timestamppb.Timestamp
	t1 := timestamp.TimeValue(req.StartTime)
	for range maxListMatchingTimesCount {
		// don't need to call GetNextTime in SideEffect because this is just a query
		res, err := s.cspec.GetNextTime(s.jitterSeed(), t1)
		if err != nil {
			// An over-excluded spec won't resolve until it's edited, so return a
			// non-retryable code: retrying would just re-burn the compute bound each call.
			return nil, ErrScheduleSpecLimitHit
		}
		t1 = res.Next
		if t1.IsZero() || t1.After(timestamp.TimeValue(req.EndTime)) {
			break

View on GitHub (pinned to bde624efd1)

Solutions

  1. Provide both StartTime and EndTime in the ListScheduleMatchingTimesRequest, e.g. timestamppb.New(time.Now()) and timestamppb.New(time.Now().Add(7*24*time.Hour)).
  2. Use the SDK's Schedule.ListTimes helper instead of hand-building the query payload so required fields are populated.
  3. Check the CLI command arguments — ensure the time range flags are actually passed.
  4. If this appears despite valid input, verify the schedule is not invalid (a nil cspec produces 'invalid schedule: ...' instead).

Example fix

// before
resp, err := scheduleHandle.query(ctx, "list-schedule-matching-times", nil)
// after
req := &workflowservice.ListScheduleMatchingTimesRequest{
    StartTime: timestamppb.New(start),
    EndTime:   timestamppb.New(end),
}
resp, err := scheduleHandle.ListMatchingTimes(ctx, req)
Defensive patterns

Strategy: validation

Validate before calling

func validMatchingTimesReq(req *workflowservice.ListScheduleMatchingTimesRequest) bool {
    return req != nil && req.StartTime != nil && req.EndTime != nil
}

Type guard

func hasTimeRange(req *workflowservice.ListScheduleMatchingTimesRequest) bool {
    return req != nil && req.GetStartTime() != nil && req.GetEndTime() != nil
}

Prevention

When it happens

Trigger: Issuing a ListScheduleMatchingExecutions / ListScheduleMatchingTimes query against a schedule workflow with a nil request, or with StartTime or EndTime unset (nil protobuf timestamps).

Common situations: CLI/SDK calls where start/end flags were not provided; programmatic query invocation constructing the request struct but forgetting timestamppb fields; tooling sending an empty query payload to the workflow.

Related errors


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