GoogleChrome/lighthouse · error · Error

Timespan already in progress

Error message

Timespan already in progress

What it means

State-machine guard in UserFlow.navigate(). The UserFlow class enforces that only one gathering operation (navigation or timespan) can be active at a time. navigate() requires both currentTimespan and currentNavigation to be undefined. This throw fires when a timespan is still active — startTimespan() was called but endTimespan() has not completed.

Source

Thrown at core/user-flow.js:127

  /**
   * @param {LH.Gatherer.GatherResult} gatherResult
   * @param {LH.UserFlow.StepFlags} [flags]
   */
  _addGatherStep(gatherResult, flags) {
    const gatherStep = {
      artifacts: gatherResult.artifacts,
      flags,
    };
    this._gatherSteps.push(gatherStep);
    this._gatherStepRunnerOptions.set(gatherStep, gatherResult.runnerOptions);
  }

  /**
   * @param {LH.NavigationRequestor} requestor
   * @param {LH.UserFlow.StepFlags} [flags]
   */
  async navigate(requestor, flags) {
    if (this.currentTimespan) throw new Error('Timespan already in progress');
    if (this.currentNavigation) throw new Error('Navigation already in progress');

    const nextFlags = this._getNextNavigationFlags(flags);
    const gatherResult = await navigationGather(this._page, requestor, {
      config: this._options?.config,
      flags: nextFlags,
    });

    this._addGatherStep(gatherResult, nextFlags);
  }

  /**
   * This is an alternative to `navigate()` that can be used to analyze a navigation triggered by user interaction.
   * For more on user triggered navigations, see https://github.com/GoogleChrome/lighthouse/blob/main/docs/user-flows.md#triggering-a-navigation-via-user-interactions.
   *
   * @param {LH.UserFlow.StepFlags} [stepOptions]
   */
  async startNavigation(stepOptions) {

View on GitHub (pinned to 9515cd4e58)

Solutions

  1. Call flow.endTimespan() before flow.navigate() or flow.startNavigation()
  2. Wrap timespan logic in try/finally to guarantee endTimespan() runs even on error
  3. Check flow.currentTimespan before navigating — if set, end the timespan first

Example fix

// before — navigate while timespan active
await flow.startTimespan();
// ... interactions ...
await flow.navigate(url); // throws

// after — end timespan first
await flow.startTimespan();
// ... interactions ...
await flow.endTimespan();
await flow.navigate(url);
Defensive patterns

Strategy: validation

Validate before calling

if (flow.currentTimespan) {
  await flow.endTimespan();
}
await flow.navigate(url);

Type guard

/** @param {UserFlow} flow @returns {boolean} */
function isTimespanActive(flow) {
  return flow.currentTimespan != null;
}

Try / catch

try {
  await flow.navigate(url);
} catch (e) {
  if (e.message === 'Timespan already in progress') {
    await flow.endTimespan();
    await flow.navigate(url);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling flow.navigate(url) or flow.startNavigation() after flow.startTimespan() without calling flow.endTimespan() first. The currentTimespan property is still set from the prior startTimespan call.

Common situations: Forgot to call endTimespan() before navigating. An exception in user interaction code between startTimespan and endTimespan prevented the end call. Using navigate() instead of endTimespan() to close a timespan by mistake.

Related errors


AI-assisted analysis of GoogleChrome/lighthouse@9515cd4e58 (2026-08-13). Data as JSON: /api/errors/b030fd24cc03b8e6. Report an issue: GitHub.