Skip to content

iOS WebKit: gantt-layout-loader throws on missing window.requestIdleCallback — global error boundary on every issues-list view #9231

@hstern

Description

@hstern

Description

Plane CE's lazy-loaded gantt-layout-loader chunk calls window.requestIdleCallback without feature detection or a polyfill. iOS WebKit (Safari, iOS Chrome, iOS Firefox — all WebKit-backed under Apple's App Store rules) has never implemented requestIdleCallback — open WebKit gap since 2016 (webkit.org#164193). The throw propagates up, React's error boundary catches it, and the user sees the generic "Looks like something went wrong" page on every issues-list / project view.

Steps to reproduce

  1. iPad (any iOS / iPadOS version), Chrome (CriOS user-agent — but iOS Safari and iOS Firefox would all fail the same way; see "Why the matrix is weird" below).
  2. Sign in to a Plane CE instance.
  3. Navigate to any project's /projects/<uuid>/issues/ page.
  4. The full error boundary fires.

Console error (from Safari Web Inspector against the iPad)

TypeError: window.requestIdleCallback is not a function.
(In 'window.requestIdleCallback(()=>{h.current&&(S.current=`${h.current.offsetHeight}px`)})', 'window.requestIdleCallback' is undefined)
— gantt-layout-loader-BH8Iumhn.js:1:5524

React Router caught the following error during render
— componentDidCatch (chunk-EPOLDU6W-PsE1YSAx.js:4:2496)

[also] Minified React error #418 (hydration failed) and #423 (client-side recovery also failed)
— both downstream of the same throw.

Why the matrix is weird (and the cause is clear)

Browser Result Why
iOS Chrome (CriOS) Error boundary UA hints route through the bundle that lazy-loads gantt-layout-loader → hits missing API
iOS Safari Works UA routes through a different bundle that doesn't load the gantt chunk
iOS Chrome with "Request Desktop Site" Works Desktop UA routes through the desktop bundle (gantt chunk apparently feature-detects or skips on the desktop layout calculator path)
macOS Safari / macOS Chrome Works Engines implement requestIdleCallback natively

So the bug has been sitting hidden behind a UA-sniffing branch that only one of three iOS browser paths exercises.

Suggested fix

Either:

A — Polyfill requestIdleCallback once at app entry:

if (typeof window.requestIdleCallback !== 'function') {
  window.requestIdleCallback = (cb) => {
    const start = Date.now();
    return setTimeout(() => cb({
      didTimeout: false,
      timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),
    }), 1);
  };
  window.cancelIdleCallback = (id) => clearTimeout(id);
}

B — Feature-detect inside gantt-layout-loader and fall back to setTimeout(fn, 0) for the height-measurement use case (which doesn't actually need idle-time semantics; it's just deferring a layout measurement).

Either is a few lines. (A) catches every other requestIdleCallback use in the codebase.

Environment

  • Plane CE v1.3.1 (self-hosted, full upstream image set)
  • Client: iPad (any), Chrome on iOS / iOS WebKit

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions