microsoft/playwright · error · Error

Invalid timezone ID: ${contextOptions.timezoneId}

Error message

Invalid timezone ID: ${contextOptions.timezoneId}

What it means

When a contextOptions.timezoneId is set, WebKit issues Page.setTimeZone; if the engine rejects the timezone string (unrecognized IANA zone), the .catch rewraps it as 'Invalid timezone ID: <id>'. WebKit only accepts valid IANA timezone identifiers.

Source

Thrown at packages/playwright-core/src/server/webkit/wkPage.ts:217

    if (bootstrapScript.length)
      promises.push(session.send('Page.setBootstrapScript', { source: bootstrapScript }));
    this._page.frames().map(frame => frame.evaluateExpression(nullProgress, bootstrapScript).catch(e => {}));
    if (contextOptions.bypassCSP)
      promises.push(session.send('Page.setBypassCSP', { enabled: true }));
    const emulatedSize = this._page.emulatedSize();
    if (emulatedSize) {
      promises.push(session.send('Page.setScreenSizeOverride', {
        width: emulatedSize.screen.width,
        height: emulatedSize.screen.height,
      }));
    }
    promises.push(session.send('Network.setExtraHTTPHeaders', { headers: headersArrayToObject(this._calculateExtraHTTPHeaders(), false /* lowerCase */) }));
    if (contextOptions.offline)
      promises.push(session.send('Network.setEmulateOfflineState', { offline: true }));
    promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: !!contextOptions.hasTouch }));
    if (contextOptions.timezoneId) {
      promises.push(session.send('Page.setTimeZone', { timeZone: contextOptions.timezoneId }).
          catch(e => { throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`); }));
    }
    if (this._page.fileChooserIntercepted())
      promises.push(session.send('Page.setInterceptFileChooserDialog', { enabled: true }));
    promises.push(session.send('Page.overrideSetting', { setting: 'FullScreenEnabled', value: !contextOptions.isMobile }));
    promises.push(session.send('Page.overrideSetting', { setting: 'NotificationsEnabled', value: !contextOptions.isMobile }));
    promises.push(session.send('Page.overrideSetting', { setting: 'PointerLockEnabled', value: !contextOptions.isMobile }));
    promises.push(session.send('Page.overrideSetting', { setting: 'InputTypeMonthEnabled', value: contextOptions.isMobile }));
    promises.push(session.send('Page.overrideSetting', { setting: 'InputTypeWeekEnabled', value: contextOptions.isMobile }));
    promises.push(session.send('Page.overrideSetting', { setting: 'FixedBackgroundsPaintRelativeToDocument', value: contextOptions.isMobile }));
    promises.push(session.send('Page.overrideSetting', { setting: 'PushAPIEnabled', value: !contextOptions.isMobile }));
    await Promise.all(promises);
  }

  private _initializeFrameSessions(frame: Protocol.Page.FrameResourceTree, promises: Promise<any>[]) {
    const session = this._targetIdToFrameSession.get(`frame-${frame.frame.id}`);
    if (session)
      promises.push(session.initialize());
    for (const childFrame of frame.childFrames || [])

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Use a canonical IANA timezone id, e.g. 'America/New_York', 'Europe/Berlin', 'UTC'.
  2. On Linux CI ensure the tzdata package is installed so the zone database is complete.
  3. Validate the id against Intl.supportedValuesOf('timeZone') (modern Node) before passing it.

Example fix

// before
const ctx = await browser.newContext({ timezoneId: 'EST' });

// after
const ctx = await browser.newContext({ timezoneId: 'America/New_York' });
Defensive patterns

Strategy: validation

Validate before calling

const known = new Set(typeof Intl !== 'undefined' && Intl.supportedValuesOf ? Intl.supportedValuesOf('timeZone') : ['UTC']);
function validTz(tz) { return !tz || known.has(tz); }

Type guard

function isIanaTimezone(tz: string): boolean {
  if (typeof Intl !== 'undefined' && Intl.supportedValuesOf) return Intl.supportedValuesOf('timeZone').includes(tz);
  return tz === 'UTC' || tz.includes('/');
}

Try / catch

try { await browser.newContext({ timezoneId }); }
catch (e) {
  if (/Invalid timezone ID/.test(e.message)) await browser.newContext({ timezoneId: 'UTC' });
  else throw e;
}

Prevention

When it happens

Trigger: Launching a context with newContext({ timezoneId: '...' }) where the value is misspelled, non-IANA, or not present in the OS tz database. Also contextOptions updates that re-apply an invalid zone.

Common situations: Typos like 'UTC+2' or 'EST' (use 'America/New_York'), 'GMT' variants, or zones absent on minimal CI images. Copying timezone strings from non-IANA sources.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/c0fbaf13b7c2137a. Report an issue: GitHub.