Skip to content

[Bug]: ERR_INSUFFICIENT_RESOURCES error on local dev #9544

Description

@brendon-codes

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/desktop

Steps to reproduce

Steps to reproduce

Affected configuration

The problem affects unbundled Electron desktop development:

vp run dev:desktop

It is most visible on a cold Vite dependency cache or fresh Electron profile. It may also appear when a project with a custom Lucide icon is rendered or when the project icon picker is opened.

Normal web development does not pass every resource through Electron's custom protocol proxy, so it may tolerate the same chunk graph without producing the SimpleURLLoaderWrapper failure.

Reproduction

  1. Check out T3 Code at or after commit f6c04c552c203350705f9ab1e47773ea736af245.

  2. Install dependencies.

  3. Start desktop development with a cold Vite dependency cache:

    vp run dev:desktop
  4. Open a project that has a custom Lucide icon, or navigate to the project settings and open the project icon picker.

  5. Observe the desktop process output and the renderer's network activity.

Depending on timing and available Chromium loader capacity, the error may occur during initial rendering, while loading the icon UI, or during shutdown with requests still in flight.

Expected behavior

Expected behavior

Desktop development should load reliably regardless of whether the Vite and Electron caches are warm or cold.

Opening the project icon picker or rendering a custom icon should request only the icon data and modules needed for the visible UI. It should not generate a request graph large enough to exhaust Electron's network loader.

Actual behavior

Actual behavior

Desktop development can fail to load renderer resources with:

Error: net::ERR_INSUFFICIENT_RESOURCES
    at SimpleURLLoaderWrapper.<anonymous> (node:electron/js2c/browser_init:2:138547)
    at SimpleURLLoaderWrapper.emit (node:events:509:28)

Possible user-visible outcomes include:

  • The main window remains blank or partially initialized.
  • Some renderer modules fail to load.
  • A custom project icon falls back or fails to appear.
  • The app eventually renders, but the terminal still reports transient resource failures.
  • Reloading produces inconsistent results depending on which resources are already cached.

The backend can remain healthy throughout the failure. Backend readiness and main-window creation may both complete successfully before renderer subresources begin failing.

Why this is not ordinary host resource exhaustion

The failure can occur while system resources are otherwise healthy. In an affected run:

  • Tens of gigabytes of memory remained available.
  • Swap was unused.
  • The process inherited a high open-file limit.
  • System-wide file-handle usage was low.
  • Disk space was plentiful.
  • The number of active TCP sockets was low.
  • The backend started and answered its readiness request successfully.
  • The Electron remote-debugging endpoint bound successfully.

ERR_INSUFFICIENT_RESOURCES is therefore describing an exhausted Chromium or Electron request-loader resource, not necessarily exhausted machine RAM or file descriptors.

Impact

Blocks work completely

Version or commit

main (introduced in f6c04c5)

Environment

Bug was found on Linux. Not tested on other platforms:

vp run dev:desktop

Logs or stack traces

Error: net::ERR_INSUFFICIENT_RESOURCES
    at SimpleURLLoaderWrapper.<anonymous> (node:electron/js2c/browser_init:2:138547)
    at SimpleURLLoaderWrapper.emit (node:events:509:28)

Screenshots, recordings, or supporting files

No response

Workaround

Miscellaneous notes and suggested fix strategies

Original report: Desktop development exhausts Electron's URL loader after customizable project icons were added

Summary

This appears to be a regression introduced by feat(web): add customizable project icons (#9137), commit f6c04c552c203350705f9ab1e47773ea736af245.

The project-icon implementation imports lucide-react/dynamic. During Vite dependency optimization, Lucide's dynamic icon registry expands into thousands of separate JavaScript chunks. Desktop development serves the renderer through T3 Code's custom t3code-dev:// protocol, where every renderer resource is forwarded to Vite using Electron.net.fetch.

The resulting burst of small requests can saturate Chromium's internal URL-loader resources. Once saturated, Electron rejects requests with ERR_INSUFFICIENT_RESOURCES. The machine itself does not need to be low on memory, file descriptors, disk space, or sockets for this error to occur.

Root cause

There are two interacting parts.

1. lucide-react/dynamic creates a very large optimized dependency graph

The customizable-icon implementation imports the dynamic Lucide entry point:

import { iconNames, type IconName } from "lucide-react/dynamic";

It also loads DynamicIcon from the same entry point:

const DynamicIcon = lazy(() =>
  import("lucide-react/dynamic").then((module) => ({
    default: module.DynamicIcon,
  })),
);

Lucide's dynamicIconImports registry contains approximately 1,900 literal dynamic imports:

const dynamicIconImports = {
  "a-arrow-down": () => import("./icons/a-arrow-down.js"),
  "a-arrow-up": () => import("./icons/a-arrow-up.js"),
  // ...
};

Vite's dependency optimizer turns this registry into an optimized entry accompanied by more than 2,000 small chunk records. For example:

lucide-react_dynamic.js
alarm-smoke-<hash>.js
ambulance-<hash>.js
rotate-3d-<hash>.js
...

In an affected cold run, Electron's HTTP cache recorded approximately 1,565 resources over several seconds. At least 1,273 of the recorded URLs were individual optimized Lucide icon chunks.

This request volume was introduced with the customizable project-icon feature and was not present in the previous ProjectFavicon implementation.

2. Desktop development proxies every resource through Electron.net.fetch

The desktop renderer loads from:

t3code-dev://app/

The custom protocol handler rewrites each request to the Vite development origin and forwards it using Electron.net.fetch:

const response =
  request.method === "GET" || request.method === "HEAD"
    ? await fetchWithTransientRetry(targetUrl.toString(), init)
    : await Electron.net.fetch(targetUrl.toString(), init);

Electron.net.fetch is implemented through Electron's SimpleURLLoaderWrapper, which is the source shown in the error stack.

The current retry helper makes three attempts with delays of 0, 50, and 150 milliseconds:

const TRANSIENT_FETCH_RETRY_DELAYS_MS = [0, 50, 150] as const;

This is useful for a small startup race, but it does not provide backpressure or bound the number of concurrent proxy requests. Under a large module burst, retries can add further pressure while the loader is already saturated. After the third failure, the helper rethrows the original native error without the request URL, making the terminal output difficult to diagnose.

Additional recovery gap

The development main-window retry set includes several transient Chromium errors:

const DEVELOPMENT_RETRYABLE_LOAD_ERROR_CODES = new Set([
  -2,   // ERR_FAILED
  -7,   // ERR_TIMED_OUT
  -9,   // ERR_UNEXPECTED
  -102, // ERR_CONNECTION_REFUSED
  -105, // ERR_NAME_NOT_RESOLVED
  -106, // ERR_INTERNET_DISCONNECTED
  -118, // ERR_CONNECTION_TIMED_OUT
]);

Chromium error -12, ERR_INSUFFICIENT_RESOURCES, is not included.

Additionally, did-fail-load ignores subframe and subresource failures. If the main document succeeds but essential JavaScript modules fail, the desktop window can remain blank or incomplete without triggering the existing main-document recovery behavior.

Adding -12 to the retry set may improve recovery for main-document failures, but it would not address the underlying request storm.

Workaround

Enabling Vite's bundled development mode substantially reduces the number of per-module requests:

T3CODE_BUNDLED_DEV=1 vp run dev:desktop

If the error disappears in bundled mode, that confirms the failure is related to unbundled module-request pressure rather than backend startup or external service availability.

This is a useful workaround and diagnostic, but desktop development should remain reliable in the default mode.

Suggested fix direction

The primary fix should prevent lucide-react/dynamic from producing or loading thousands of optimized icon chunks.

Options worth evaluating include:

  1. Exclude lucide-react/dynamic from Vite dependency optimization so the original dynamic registry is preserved and only the selected icon module is requested.
  2. Keep icon names in generated static data that does not import Lucide's dynamic loader. Load the rendering implementation only after an actual custom icon needs to be displayed.
  3. Use a bounded, curated icon registry instead of exposing every Lucide icon.
  4. Bundle the dynamic icon implementation into one controlled chunk if supporting the complete Lucide catalog is required.
  5. Add bounded concurrency or queuing to the Electron custom-protocol proxy as a defense against future large request bursts.

The first fix should be applied at the project-icon or Vite boundary. Increasing retry counts or OS resource limits would only mask the regression.

Diagnostics that would improve future failures

When the final Electron.net.fetch attempt fails, log:

  • The request method.
  • The rewritten target URL.
  • The original custom-protocol URL.
  • The number of attempts.
  • The native Electron error code.

The current bare rethrow produces only the SimpleURLLoaderWrapper stack and hides which renderer resource caused the failure.

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

    bugSomething is broken or behaving incorrectly.needs-triageIssue needs maintainer review and initial categorization.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions