Skip to content

Work Items / Modules list crashes on Safari & iOS browsers: window.requestIdleCallback is not a function #9439

Description

@sadpaperclip

Title: Work Items / Modules list crashes on Safari & iOS browsers: window.requestIdleCallback is not a function

Body:

Bug description

Opening the "Work Items" list or a Module's detail view crashes the app with a generic
"Looks like something went wrong!" error screen on Safari (macOS) and on any iOS browser
(Safari, Chrome/Brave/etc. on iOS — all forced to use WebKit by Apple's platform policy).

Root cause

gantt-layout-loader.js calls window.requestIdleCallback(...) without a feature check
or fallback. WebKit/Safari has never implemented requestIdleCallback (long-standing,
unresolved WebKit position — see https://bugs.webkit.org/show_bug.cgi?id=180002), so the
call throws:

TypeError: window.requestIdleCallback is not a function.
(In 'window.requestIdleCallback(()=>{h.current&&(S.current=`${h.current.offsetHeight}px`)})',
'window.requestIdleCallback' is undefined)

This is uncaught and bubbles up through React Router's render error boundary, producing the
generic error screen.

Environment

  • Self-hosted Community Edition (Docker Compose), makeplane/plane-frontend:stable
  • Reproduced on: Safari (macOS), Safari/Brave/Chrome (iOS/iPadOS)
  • Not reproduced on Chromium-based desktop browsers (which do implement requestIdleCallback)

Steps to reproduce

  1. Self-host Plane CE, create a project with a Module or a few Work Items
  2. Open the instance in Safari (any Apple platform)
  3. Navigate to the project's "Work Items" list, or open a Module's detail view
  4. Page loads briefly, then the global error boundary fires

Suggested fix

Add a small guard/polyfill for requestIdleCallback (fallback to setTimeout), e.g.:

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

Either bundled globally (e.g. injected once in index.html / entry point) or applied at the
specific call site in the Gantt/Timeline layout code.

Happy to open a PR with the polyfill if that's a welcome approach — just wanted to confirm the
preferred fix location first (global shim vs. call-site guard).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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