nautechsystems/nautilus_trader · error

Cannot execute time-range RequestJoin without a child window

Error message

Cannot execute time-range RequestJoin without a child window

What it means

A time-range RequestJoin must be able to yield an initial child window; if DefaultTimeRangeGenerator.can_yield_initial_window() is false, there is no child interval to fan out to and execution aborts. This prevents joining against an empty/degenerate time-range pipeline.

Source

Thrown at crates/data/src/engine/time_range.rs:94

            | RequestCommand::BookDepth(_)
            | RequestCommand::Join(_)
    )
}

impl DataEngine {
    pub(super) fn execute_time_range_pipeline_request(
        &mut self,
        req: RequestCommand,
    ) -> anyhow::Result<()> {
        let request_id = *req.request_id();
        let (start_dt, end_dt, start_ns, end_ns) = self.bound_time_range_pipeline_dates(&req)?;
        let ts_init = self.clock.borrow().timestamp_ns();
        let parent =
            time_range_parent_request_with_dates(req, Some(start_dt), Some(end_dt), ts_init);
        let response_client_id = self.time_range_pipeline_response_client_id(&parent);
        let generator = DefaultTimeRangeGenerator::new(request_params(&parent), start_ns, end_ns)?;
        if matches!(parent, RequestCommand::Join(_)) && !generator.can_yield_initial_window() {
            anyhow::bail!("Cannot execute time-range RequestJoin without a child window");
        }

        self.time_range_pipeline_requests.insert(
            request_id,
            TimeRangePipelineState {
                parent,
                generator,
                start_ns,
                end_ns,
                response_client_id,
                data_count: 0,
                last_response: None,
            },
        );

        if let Err(e) = self.dispatch_next_time_range_pipeline_request(request_id, None) {
            self.abort_time_range_pipeline(request_id);
            return Err(e);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the Join request's start_ns < end_ns so at least one window exists
  2. Provide valid durations_seconds (positive, at least 1ns) in the request params
  3. If a Join with no child window is legitimately possible, handle the empty case before calling execute_time_range_pipeline_request

Example fix

// before
let params = Params::from([("durations_seconds", json!([0]))]); // no valid window
// after
let params = Params::from([("durations_seconds", json!([1.0]))]);
assert!(start_ns < end_ns);
Defensive patterns

Strategy: validation

Validate before calling

assert!(start_ns < end_ns, "Join window must be non-empty");
assert!(durations.iter().all(|d| d.is_none() || *d >= 1.0));

Prevention

When it happens

Trigger: Executing a time-range pipeline request whose parent is RequestCommand::Join while its configured windows (start/end and durations_seconds) produce no initial window — e.g. a zero-width or inverted window, or durations that filter out every candidate interval.

Common situations: Configuring a Join request with start == end; providing durations_seconds values that discard all windows; passing wrong params so the generator has no intervals to emit at t0.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/25231cb81fb49e9f. Report an issue: GitHub.