denoland/deno · error · TypeError

Options cannot be passed with "endMark"

Error message

Options cannot be passed with "endMark"

What it means

performance.measure(name, startOrMeasureOptions, endMark) has two mutually exclusive shapes: either a third-argument endMark string, or a second-argument options object describing start/end/duration. Deno throws a TypeError from ext/web/15_performance.js when the options object is a non-empty object AND an endMark string was also passed. The check only fires for real objects with at least one own key.

Source

Thrown at ext/web/15_performance.js:754

    );

    startOrMeasureOptions = webidl.converters
      ["DOMString or PerformanceMeasureOptions"](
        startOrMeasureOptions,
        prefix,
        "Argument 2",
      );

    if (endMark !== undefined) {
      endMark = webidl.converters.DOMString(endMark, prefix, "Argument 3");
    }

    if (
      startOrMeasureOptions && typeof startOrMeasureOptions === "object" &&
      ObjectKeys(startOrMeasureOptions).length > 0
    ) {
      if (endMark) {
        throw new TypeError('Options cannot be passed with "endMark"');
      }
      if (
        ReflectHas(startOrMeasureOptions, "start") &&
        ReflectHas(startOrMeasureOptions, "duration") &&
        ReflectHas(startOrMeasureOptions, "end")
      ) {
        throw new TypeError(
          'Cannot specify "start", "end", and "duration" together in options',
        );
      }
    }
    let endTime;
    if (endMark) {
      endTime = convertMarkToTimestamp(endMark);
    } else if (
      typeof startOrMeasureOptions === "object" &&
      ReflectHas(startOrMeasureOptions, "end")
    ) {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pick one shape: use endMark as argument 3 with at most a start mark/number as argument 2, or put everything in the options object.
  2. To express start-to-end, prefer options: performance.measure("m", { start: "mark1", end: "mark2" }).
  3. If argument 2 is only a start mark name, that is fine with endMark — the conflict is only with a non-empty options object.
  4. In wrapper functions, branch: if the caller supplied options, ignore/forbid endMark.

Example fix

// before
performance.measure("m", { start: "a" }, "b");

// after
performance.measure("m", { start: "a", end: "b" });
Defensive patterns

Strategy: validation

Validate before calling

function measure(name, options, endMark) {
  const hasOptions =
    options != null && typeof options === "object" &&
    Object.keys(options).length > 0;
  if (hasOptions && endMark !== undefined) {
    // move endMark into options instead of throwing
    options = { ...options, end: endMark };
    endMark = undefined;
  }
  return performance.measure(name, options, endMark);
}

Type guard

function isMeasureOptions(v) {
  return v != null && typeof v === "object" && !Array.isArray(v);
}

Try / catch

try { performance.measure(n, opts, endMark); } catch (e) { if (e instanceof TypeError && e.message.includes("endMark")) performance.measure(n, { ...opts, end: endMark }); else throw e; }

Prevention

When it happens

Trigger: performance.measure("m", { start: 0 }, "endMark") — options plus a mark name; passing a default empty-ish options object that later gained keys; wrapper functions that accept both parameters and forward them unconditionally.

Common situations: Generalizing a measure() helper so it accepts options and endMark, then calling it with both; refactoring from the (startMark, endMark) form to the options form without removing the third argument; copy-pasting from mixed examples.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/d1166c8d73b41a10. Report an issue: GitHub.