Why your npm package fails in base44
Base44 does not document an npm allowlist. Its changelog says you can "Bring any web library from NPM into your Base44 apps," and you add a package by naming it in the AI chat and approving the install. When a package seems rejected, it is failing for a dependency or compatibility reason, which the AI chat often explains in its messages. The common causes: frontend code runs in the browser, so packages that need Node.js built-ins or native binaries cannot run there; backend functions run on Deno, need the npm: prefix on package imports, and do not support some Node.js built-ins such as fs, path, process and crypto; and a transitive dependency can carry either problem into a package that looks fine. Four fixes ship the feature: swap to a browser- or Deno-compatible package, rewrite the call against a built-in Web API, move server-side work into a backend function with npm: imports, or host the package in a separate Node.js service that a backend function calls over HTTPS.
You asked the Base44 AI chat to add a feature. The code it generated imported an npm package — say puppeteer, sharp, bcrypt or canvas — and something broke: the install stalled on pending, the chat reported a dependency or compatibility issue, the preview stopped building, or a backend function would not save or start.
It is tempting to read that as Base44 blocking the package. Base44's npm guide describes no such gate. It points you to npmjs.com "to see all available public packages," and its first troubleshooting step for a failed install is to "Check the chat messages for error details." The package failed because of what it needs, where it was asked to run, or what it pulled in with it.
The good news: most of these failures have a clean swap. The bad news: a few packages genuinely need a full Node.js environment, and the right answer for those is a small service outside the app.
What actually causes an npm package rejected error in base44
Four causes produce the same "it won't install" or "it won't run" symptom. Knowing which one you have tells you which fix path to take.
A Node.js-only package in browser code. Base44 apps are React frontends built with Vite, so page and component code runs in your users' browsers. Packages that read the file system (fs), spawn processes (child_process), open raw sockets (net, tls) or load a compiled native binary (sharp, bcrypt, canvas) have nothing to run on there. The build or the page fails as soon as the bundler or the browser reaches that code.
An unsupported import in a backend function. Backend functions run on Deno, a TypeScript runtime, not on Node.js. Packages are imported with an npm: specifier, and an import without the prefix is one of the mistakes Base44's function-creation reference calls out. Base44's troubleshooting guide adds that "The Deno runtime does not support some Node.js built ins or browser only APIs," listing fs, path, process and crypto, plus DOM or window APIs. A function that breaks these rules can fail to save or deploy with ISOLATE_INTERNAL_FAILURE.
A transitive dependency. The package you asked for may be fine while one of its own dependencies needs a native binary or a Node.js built-in. The error then names a package you never installed, which makes the connection hard to see. A common shape: an authentication helper that depends on bcrypt for password hashing.
A module-format or version conflict. Vite builds ES modules, and some older CommonJS-only packages fail in that build with errors such as require is not defined. Two packages that need different major versions of the same peer dependency can also refuse to install together. Base44's own advice for choosing packages names the same risk: lightweight, focused packages "reduce potential conflicts and bloat."
Sources: Base44's npm packages guide, backend functions overview and troubleshooting page (all read 17 Oct 2026).
How to confirm which failure you have (reproduction)
- Open the AI chat and read the messages around the failed install. Base44's npm guide says the chat sometimes explains the dependency or compatibility issue. Copy that explanation, plus the package name and version.
- Check that the request named the exact package and version, and that you clicked Approve if the chat asked for approval. A request stuck on pending can simply be waiting for that approval.
- Find out where the import lives. An import in a page or component runs in the browser; an import in a backend function runs on Deno.
- For a frontend failure, open the preview with the browser console open and note the first error: a missing module such as
fs,require is not defined, or a native binding that fails to load. - For a backend function, check that every package import uses the
npm:prefix, and whether the function or its packages touchfs,path,processorcrypto. AnISOLATE_INTERNAL_FAILUREon save or deploy points here. - To see the dependency tree, ask the AI chat which packages are installed, or, if the app syncs with GitHub (Builder plan or higher), clone the repository and run
npm ls <package-name>to find which direct dependency pulled the offender in. - Look the package up on npmjs.com. Its README and
package.jsonshow whether it targets the browser, needs Node.js or ships native code, and which license it carries.
If the chat's explanation and these checks agree, move to the fix paths below.
How to fix base44 npm package rejected error — four workable paths
Pick the path that matches the cause and your timeline.
Path A: Swap to a browser- or Deno-compatible package (fastest, recommended default)
When the failing package sits in a well-served category, a compatible alternative usually covers the same capability. The swap is often an import rewrite plus a few API changes.
Common swap pairs:
PASSWORD HASHING
bcrypt (native binary) → bcryptjs (pure JavaScript)
JWT SIGNING AND VERIFYING
jsonwebtoken (Node.js crypto) → jose (Web Crypto; browser and Deno)
HTTP CLIENTS
node-fetch, request → built-in fetch
IMAGE RESIZING IN THE BROWSER
sharp (native binary) → browser-image-compression or the Canvas API
SQLITE
sqlite3, better-sqlite3 (native) → sql.js (WebAssembly)
PDF FROM HTML
puppeteer (downloads Chromium) → jspdf or react-pdf in the browser, or Path D
The pattern: prefer actively maintained packages that say they support browsers or Deno. Base44's npm guide lists the checks worth running before you install: download statistics, documentation quality, license type, bundle size and dependencies, update activity, and community input.
// Before — needs Node's crypto module, fails in the browser
import jwt from "jsonwebtoken";
// After — built on Web Crypto, runs in the browser and on Deno
import { jwtVerify } from "jose";
In a backend function, the same package is imported with the prefix: import { jwtVerify } from "npm:jose";.
Path B: Rewrite against a built-in Web API (most durable)
When the browser and Deno already ship the capability, remove the package. This is the most durable fix because it removes a dependency rather than swapping one, and the same code runs in a page and in a backend function.
// Before — an extra dependency for a plain GET request
import axios from "axios";
const res = await axios.get("/api/orders", { params: { status: "open" } });
return res.data;
// After — built-in fetch (no dependency)
const url = new URL("/api/orders", window.location.origin);
url.searchParams.set("status", "open");
const res = await fetch(url);
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
return await res.json();
Common categories where a Web API wins outright:
- HTTP requests —
fetchcovers everyday requests, headers, JSON bodies and query strings. - Date and number formatting —
Intl.DateTimeFormat,Intl.NumberFormat,Intl.RelativeTimeFormat. - UUIDs —
crypto.randomUUID(). - Hashing and signing —
crypto.subtlecovers SHA-256, HMAC and AES, and it is the replacement for Node'scryptomodule in a backend function. - Deep cloning —
structuredClone(obj)replaces lodashcloneDeepfor most JSON-safe data. - Debounce and throttle — a few lines of plain code each, instead of a utility library for two functions.
Path C: Move server-side work into a backend function
Some work does not belong in the browser at all: calling an API with a secret key, verifying a signature, processing a webhook. Move it into a backend function (backend functions need the Builder plan or higher), import its packages with npm: specifiers, and read secrets inside the handler with secrets.get() from the built-in base44:runtime module. Call the function from your frontend with base44.functions.invoke().
// Backend function (Deno): npm packages need the npm: prefix
import { createClientFromRequest } from "npm:@base44/sdk";
import { format } from "npm:date-fns";
Deno.serve(async (req) => {
const base44 = createClientFromRequest(req);
const user = await base44.auth.me();
if (!user) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
return Response.json({ today: format(new Date(), "yyyy-MM-dd") });
});
// Frontend: call the function through the SDK
import { base44 } from "@/api/base44Client";
const result = await base44.functions.invoke("formatToday", {});
A backend function is the right home for server work, but it is not full Node.js: a package that needs fs, child_process or a native binary will still fail on Deno. For those, use Path D.
Path D: Host the package in a separate service (escape valve for full Node.js)
If the package fundamentally needs a full Node.js environment — headless Chromium for PDF rendering, a native image library, an SDK that spawns processes — host it in a small Node.js service you control and expose one HTTPS endpoint. Call that endpoint from a backend function, not from the browser, so the service key stays in a Base44 secret instead of your frontend code.
// Backend function: call your external PDF service with a secret key
import { createClientFromRequest } from "npm:@base44/sdk";
import { secrets } from "base44:runtime";
Deno.serve(async (req) => {
const base44 = createClientFromRequest(req);
const user = await base44.auth.me();
if (!user) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
const token = secrets.get("PDF_SERVICE_TOKEN");
if (!token) {
return Response.json({ error: "PDF_SERVICE_TOKEN is not set" }, { status: 500 });
}
const res = await fetch("https://pdf.your-microservice.com/render", {
method: "POST",
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
body: JSON.stringify(await req.json()),
});
if (!res.ok) {
return Response.json({ error: `PDF generation failed: ${res.status}` }, { status: 502 });
}
return Response.json(await res.json());
});
The external service handles the Node.js-only dependency; your Base44 app handles the UI and calls through the function. Keep each call well inside the 5-minute maximum execution time Base44 sets for backend functions. This path adds operational cost (a second deploy target, monitoring, scaling), so it is the right answer for genuinely irreplaceable packages and the wrong one for routine swaps that Path A ships in an hour.
In all paths: pin and document your dependency choice
Once a fix ships, pin the working version — ask the AI chat for the exact version you tested, and in a backend function write npm:package@version, the way Base44's own examples import npm:@base44/sdk@0.8.44 — and add a short comment explaining why the original package was avoided. A future prompt or maintainer will otherwise re-introduce it and hit the same error. You can also ask the AI chat to update or remove a package later.
// jsonwebtoken needs Node's crypto module, which Base44's Deno runtime
// does not support. jose uses Web Crypto; do not swap back.
import { jwtVerify } from "npm:jose";
How long does it take to fix base44 npm package rejection?
Timeline depends on which path applies.
- Path A (swap to a compatible package): 20-60 minutes for the import rewrite and signature adaptation. Add 30-90 minutes for testing if the package touched multiple screens. Total: 1-2 hours.
- Path B (Web API rewrite): 1-3 hours depending on how many call sites used the package. Add 1-2 hours for testing. Slower than a swap but more durable.
- Path C (move into a backend function): comparable to a Path B rewrite, plus wiring the frontend call to the new function.
- Path D (separate service): 1-3 days of build work for the new service, plus 1-2 hours of integration work in the Base44 app. Add 2-4 hours for monitoring and deploy automation. Total: 2-5 days for the first service; later ones are faster because the operational pattern is reused.
In all paths, ship a working fix first and pursue the longer-term cleaner path in parallel.
DIY vs hire decision
DIY this if: The failing package falls in a category with obvious swaps (HTTP, dates, hashing, JWTs, utilities), you are comfortable rewriting against a slightly different API, and you have basic testing coverage. Path A swaps are reliably DIY-friendly. Web API rewrites (Path B) are DIY if you are familiar with modern Web Platform APIs.
Hire help if: The package is central to multiple screens and its API differences would cascade into a large refactor, the package category has no obvious swap (machine learning, PDF rendering, audio processing), or the right answer is Path D and you do not have experience standing up production services. A fix-sprint engagement typically covers a single package fix end-to-end including the swap, the test coverage, and a documented rollback path.
Need this fixed before your next sprint?
For a single failing-package fix, our fix-sprint engagement covers the swap, Web API rewrite, backend function move, or external service setup in a fixed-price block. We handle the dependency audit, the alternative selection, the implementation, and the regression testing. For larger dependency conflicts (5+ failing packages, an external service architecture, recurring compatibility friction across a team), the complex-fix engagement plans a portfolio-level dependency strategy.
Start a fix-sprint engagement for an npm package fix
Related problems
- Vendor lock-in via SDK dependency — the broader coupling question when more of your app depends on platform-specific code than you planned, and how to decouple before it gets worse.
- Hallucinated fields and fake endpoints — the related AI-agent failure where the generated code imports packages or endpoints that do not exist.
- Context window exceeded and AI forgets prior work — why the agent sometimes re-imports a package you already swapped out after a long session.