[GH-ISSUE #292] deepagents fails to build with Vite/Rollup in browser targets due to named Node.js builtin imports #245

Closed
opened 2026-06-05 17:21:14 -04:00 by yindo · 5 comments
Owner

Originally created by @BjoernSchotte on GitHub (Mar 10, 2026).
Original GitHub issue: https://github.com/langchain-ai/deepagentsjs/issues/292

Originally assigned to: @christian-bromann on GitHub.

Description

Importing anything from deepagents in a Vite browser build fails with hard errors. The built dist/index.js is a single flat bundle that contains named imports from Node.js builtins. Vite externalizes node:* modules for browser compatibility, but named imports (as opposed to default imports) cause Rollup to fail because it verifies that the binding exists in the externalized module.

The library's architecture already supports browser use — BackendProtocol, StateBackend, StoreBackend, all middleware, and createDeepAgent have zero Node.js dependencies. The issue is purely packaging: the single barrel export bundles Node-only modules alongside browser-safe ones into a flat dist/index.js.

Package version: 1.8.1 (latest on npm)
Vite version: 7.3.1 (latest)

Reproduction

Minimal repro: https://github.com/BjoernSchotte/deepagents-vite-repro

git clone https://github.com/BjoernSchotte/deepagents-vite-repro.git
cd deepagents-vite-repro
bun install   # or npm install
bun run build # or npx vite build

Actual Behavior

The build fails with 3 sequential hard errors (each one hidden behind the previous):

Error 1: node:async_hooks (from @langchain/langgraph, not deepagents)

"AsyncLocalStorage" is not exported by "__vite-browser-external"
  import { AsyncLocalStorage } from "node:async_hooks";

Error 2: "path" (from deepagents backends/utils.ts)

After stubbing node:async_hooks via resolve.alias:

"basename" is not exported by "__vite-browser-external"
  import { basename } from "path";

Error 3: node:child_process (from deepagents backends/filesystem.ts / local-shell.ts)

After also stubbing path:

"spawn" is not exported by "__vite-browser-external"
  import cp, { spawn } from "node:child_process";

After stubbing all 6 node:* modules + path via Vite resolve.alias, the build succeeds (840 modules, zero errors). The Node-only code paths are dead code when using createDeepAgent + StateBackend.

Why Named Imports Fail But Default Imports Don't

Vite externalizes unresolvable modules as __vite-browser-external (an empty object). Default imports (import fs from "node:fs") succeed because fs just becomes {} — Rollup doesn't verify property access at build time. Named imports (import { spawn } from "node:child_process") fail because Rollup verifies the binding exists as an export.

The built dist/index.js lines 5–18:

import micromatch from "micromatch";             // ✅ default import, warning only
import { basename } from "path";                 // ❌ named import, HARD FAIL
import fs from "node:fs/promises";               // ✅ default import, warning only
import fs$1 from "node:fs";                      // ✅ default import, warning only
import path from "node:path";                    // ✅ default import, warning only
import cp, { spawn } from "node:child_process"; // ❌ named import `spawn`, HARD FAIL
import fg from "fast-glob";                      // ✅ default import, warning only
import os from "node:os";                        // ✅ default import, warning only

Only 2 of the 14 Node.js-related imports in dist/index.js cause hard build failures.

Suggested Fix

Minimal fix (one-line change)

Replace import { basename } from "path" in backends/utils.ts (line 10) with an inline function:

- import { basename } from "path";
+ function basename(p: string): string {
+   const i = p.lastIndexOf("/");
+   return i === -1 ? p : p.slice(i + 1);
+ }

This eliminates Error 2. Error 3 (spawn) would still require consumer-side aliasing, but consumers would only need to stub node:child_process (and node:async_hooks which is a @langchain/langgraph issue, not deepagents).

Proper fix: deepagents/browser subpath export

Add a src/browser.ts barrel that re-exports only browser-safe modules (everything except config.ts, FilesystemBackend, LocalShellBackend, skills/loader.ts, middleware/agent-memory.ts). Add a second tsdown entry point and update the exports map:

"./browser": {
  "import": { "types": "./dist/browser.d.ts", "default": "./dist/browser.js" }
}

This would let browser consumers write:

import { createDeepAgent, StateBackend } from "deepagents/browser";

with zero Vite configuration needed.

Workaround

Add resolve.alias entries in vite.config.ts to stub the Node builtins with modules that provide the named exports Rollup needs:

resolve: {
  alias: {
    'node:async_hooks': './src/stubs/async-hooks.ts',   // for @langchain/langgraph
    'node:fs/promises': './src/stubs/fs-promises.ts',
    'node:fs': './src/stubs/fs.ts',
    'node:path': './src/stubs/path.ts',
    'node:child_process': './src/stubs/child-process.ts',
    'node:os': './src/stubs/os.ts',
    'path': './src/stubs/path.ts',
  }
}

The stubs provide the named exports so the build succeeds; the Node-only code is dead code when using createDeepAgent + StateBackend.

Originally created by @BjoernSchotte on GitHub (Mar 10, 2026). Original GitHub issue: https://github.com/langchain-ai/deepagentsjs/issues/292 Originally assigned to: @christian-bromann on GitHub. ## Description Importing anything from `deepagents` in a Vite browser build fails with hard errors. The built `dist/index.js` is a single flat bundle that contains **named imports** from Node.js builtins. Vite externalizes `node:*` modules for browser compatibility, but named imports (as opposed to default imports) cause Rollup to fail because it verifies that the binding exists in the externalized module. The library's architecture already supports browser use — `BackendProtocol`, `StateBackend`, `StoreBackend`, all middleware, and `createDeepAgent` have zero Node.js dependencies. The issue is purely packaging: the single barrel export bundles Node-only modules alongside browser-safe ones into a flat `dist/index.js`. **Package version:** 1.8.1 (latest on npm) **Vite version:** 7.3.1 (latest) ## Reproduction Minimal repro: **https://github.com/BjoernSchotte/deepagents-vite-repro** ```bash git clone https://github.com/BjoernSchotte/deepagents-vite-repro.git cd deepagents-vite-repro bun install # or npm install bun run build # or npx vite build ``` ## Actual Behavior The build fails with **3 sequential hard errors** (each one hidden behind the previous): ### Error 1: `node:async_hooks` (from `@langchain/langgraph`, not deepagents) ``` "AsyncLocalStorage" is not exported by "__vite-browser-external" import { AsyncLocalStorage } from "node:async_hooks"; ``` ### Error 2: `"path"` (from deepagents `backends/utils.ts`) After stubbing `node:async_hooks` via `resolve.alias`: ``` "basename" is not exported by "__vite-browser-external" import { basename } from "path"; ``` ### Error 3: `node:child_process` (from deepagents `backends/filesystem.ts` / `local-shell.ts`) After also stubbing `path`: ``` "spawn" is not exported by "__vite-browser-external" import cp, { spawn } from "node:child_process"; ``` After stubbing all 6 `node:*` modules + `path` via Vite `resolve.alias`, the build succeeds (840 modules, zero errors). The Node-only code paths are dead code when using `createDeepAgent` + `StateBackend`. ## Why Named Imports Fail But Default Imports Don't Vite externalizes unresolvable modules as `__vite-browser-external` (an empty object). Default imports (`import fs from "node:fs"`) succeed because `fs` just becomes `{}` — Rollup doesn't verify property access at build time. Named imports (`import { spawn } from "node:child_process"`) fail because Rollup verifies the binding exists as an export. The built `dist/index.js` lines 5–18: ```js import micromatch from "micromatch"; // ✅ default import, warning only import { basename } from "path"; // ❌ named import, HARD FAIL import fs from "node:fs/promises"; // ✅ default import, warning only import fs$1 from "node:fs"; // ✅ default import, warning only import path from "node:path"; // ✅ default import, warning only import cp, { spawn } from "node:child_process"; // ❌ named import `spawn`, HARD FAIL import fg from "fast-glob"; // ✅ default import, warning only import os from "node:os"; // ✅ default import, warning only ``` Only 2 of the 14 Node.js-related imports in `dist/index.js` cause hard build failures. ## Suggested Fix ### Minimal fix (one-line change) Replace `import { basename } from "path"` in `backends/utils.ts` (line 10) with an inline function: ```diff - import { basename } from "path"; + function basename(p: string): string { + const i = p.lastIndexOf("/"); + return i === -1 ? p : p.slice(i + 1); + } ``` This eliminates Error 2. Error 3 (`spawn`) would still require consumer-side aliasing, but consumers would only need to stub `node:child_process` (and `node:async_hooks` which is a `@langchain/langgraph` issue, not deepagents). ### Proper fix: `deepagents/browser` subpath export Add a `src/browser.ts` barrel that re-exports only browser-safe modules (everything except `config.ts`, `FilesystemBackend`, `LocalShellBackend`, `skills/loader.ts`, `middleware/agent-memory.ts`). Add a second tsdown entry point and update the `exports` map: ```json "./browser": { "import": { "types": "./dist/browser.d.ts", "default": "./dist/browser.js" } } ``` This would let browser consumers write: ```ts import { createDeepAgent, StateBackend } from "deepagents/browser"; ``` with zero Vite configuration needed. ## Workaround Add `resolve.alias` entries in `vite.config.ts` to stub the Node builtins with modules that provide the named exports Rollup needs: ```ts resolve: { alias: { 'node:async_hooks': './src/stubs/async-hooks.ts', // for @langchain/langgraph 'node:fs/promises': './src/stubs/fs-promises.ts', 'node:fs': './src/stubs/fs.ts', 'node:path': './src/stubs/path.ts', 'node:child_process': './src/stubs/child-process.ts', 'node:os': './src/stubs/os.ts', 'path': './src/stubs/path.ts', } } ``` The stubs provide the named exports so the build succeeds; the Node-only code is dead code when using `createDeepAgent` + `StateBackend`.
yindo added the bugtriage: high-impact labels 2026-06-05 17:21:14 -04:00
yindo closed this issue 2026-06-05 17:21:14 -04:00
Author
Owner

@dhrumil83 commented on GitHub (Mar 16, 2026):

We are currently using LangGraph to run AI agent orchestration fully in the browser, where agents interact directly with application state (maps, layers, UI context, etc.), and have run into similar constraints across multiple packages.

Sharing some context on how we are approaching browser-side agent architectures in case useful:

Blog: https://www.esri.com/arcgis-blog/products/js-api-arcgis/developers/introducing-ai-components-beta-in-the-js-maps-sdk
Docs: https://developers.arcgis.com/javascript/latest/agentic-apps/ai-introduction/

<!-- gh-comment-id:4070669430 --> @dhrumil83 commented on GitHub (Mar 16, 2026): We are currently using LangGraph to run AI agent orchestration fully in the browser, where agents interact directly with application state (maps, layers, UI context, etc.), and have run into similar constraints across multiple packages. Sharing some context on how we are approaching browser-side agent architectures in case useful: Blog: https://www.esri.com/arcgis-blog/products/js-api-arcgis/developers/introducing-ai-components-beta-in-the-js-maps-sdk Docs: https://developers.arcgis.com/javascript/latest/agentic-apps/ai-introduction/
Author
Owner

@BjoernSchotte commented on GitHub (Mar 18, 2026):

@christian-bromann any news on this? Anything I can help with?

<!-- gh-comment-id:4083195357 --> @BjoernSchotte commented on GitHub (Mar 18, 2026): @christian-bromann any news on this? Anything I can help with?
Author
Owner

@3ddelano commented on GitHub (Mar 20, 2026):

hey @BjoernSchotte I'm also facing similar issue. Do you perhaps have a github repo with a fix? I tried using the resolve alias but still encountering issues.

<!-- gh-comment-id:4096001307 --> @3ddelano commented on GitHub (Mar 20, 2026): hey @BjoernSchotte I'm also facing similar issue. Do you perhaps have a github repo with a fix? I tried using the resolve alias but still encountering issues.
Author
Owner

@BjoernSchotte commented on GitHub (Mar 20, 2026):

hey Björn Schotte (@BjoernSchotte) I'm also facing similar issue. Do you perhaps have a github repo with a fix? I tried using the resolve alias but still encountering issues.

Had a call with @christian-bromann - the better way would be to have the vendor create a fix for that (they have it on their radar already bc goal is that deepagentsjs should fully run in the browser) so that it also goes through their eval systems etc. That's why I was asking in the newer comment.

<!-- gh-comment-id:4097386015 --> @BjoernSchotte commented on GitHub (Mar 20, 2026): > hey [Björn Schotte (@BjoernSchotte)](https://github.com/BjoernSchotte) I'm also facing similar issue. Do you perhaps have a github repo with a fix? I tried using the resolve alias but still encountering issues. Had a call with @christian-bromann - the better way would be to have the vendor create a fix for that (they have it on their radar already bc goal is that deepagentsjs should fully run in the browser) so that it also goes through their eval systems etc. That's why I was asking in the newer comment.
Author
Owner

@hntrl commented on GitHub (Jun 2, 2026):

Quick update: this is now addressed in PR #574. Thanks for the detailed repro and write-up!

Once this PR merges and releases, you should be importing via:

import { createDeepAgent, StateBackend } from "deepagents/browser";
<!-- gh-comment-id:4605069304 --> @hntrl commented on GitHub (Jun 2, 2026): Quick update: this is now addressed in PR #574. Thanks for the detailed repro and write-up! Once this PR merges and releases, you should be importing via: ```ts import { createDeepAgent, StateBackend } from "deepagents/browser"; ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/deepagentsjs#245