Skip to content

A single failed sass-embedded compiler spawn permanently breaks all scss compilation until server restart #23374

Description

@lopno

Describe the bug

When sass-embedded is used, one transient failure to spawn its compiler child process permanently breaks all scss compilation for the lifetime of the dev server — every subsequent scss transform fails with the same error (in our app: Pre-transform error: [sass] spawn EBADF repeated for every scss module, ~240 times) until the server is restarted.

Two things combine into the permanent failure:

  1. initAsyncCompiler() resolves before the child-process spawn settles, so it never rejects — a failed spawn only surfaces later, as a rejection of each compileStringAsync() call against the dead process.
  2. makeScssWorker caches the compiler forever (compilerPromise ??= sass.initAsyncCompiler() in packages/vite/src/node/plugins/css.ts) and never disposes/re-creates it on failure, so the dead compiler is handed to every subsequent compile.

The trigger in our case is an intermittent macOS posix_spawn EBADF under file-descriptor churn — a dev server for a large app holds ~10k fds, and cold dep-optimizer runs at startup reliably created the churn window (the failure correlated 1:1 with invalidated optimizer caches across our runs; warm caches never hit it). The trigger is environmental and racy, but the amplification — never respawning a dead compiler — is deterministic and visible in the source.

This is the same failure reported in #20825 (closed as unplanned, conversation locked), which is why I'm opening a fresh issue: we root-caused it and have a fix PR.

Reproduction

https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/css.ts

Steps to reproduce

The spawn race itself is environmental (macOS, high fd pressure), so there is no minimal repro that fails on demand — but the bricking behavior can be shown by making the first spawn fail by any means:

  1. Any project with an scss file and sass-embedded installed.
  2. Make sass.initAsyncCompiler()'s underlying child_process.spawn fail once (e.g. intercept and throw a spawn EBADF-shaped error, or reproduce organically on macOS by starting a dev server with a large module graph and a cold optimizer cache).
  3. Every scss request now fails with the same spawn error; only a server restart recovers. Expected: Vite drops the dead compiler and retries with a fresh process.

Field reports of the same signature: #20825, plus multiple reports in our organization on macOS 15/26 with Node 22–24.

System Info

System:
  OS: macOS 15.x / 26.x (Apple Silicon)
Binaries:
  Node: 22.x – 24.6.0 (reproduced across all)
npmPackages:
  vite: 8.1.5 (also present on main)
  sass-embedded: 1.100.0

Used Package Manager

yarn

Logs

2:57:04 PM [vite] (client) Pre-transform error: [sass] spawn EBADF
  Plugin: vite:css
  File: /Users/…/apps/agent-interface/src/app/utils/theme/theme.scss (x240)

(the (x240) is one failure per scss module in the graph — a single dead compiler, replayed forever)

Validations

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions