Why base44 rejects your npm package
Base44 returns an npm package rejected error because the platform enforces a curated dependency allowlist — not every npm package is permitted inside the build sandbox. Rejections fire when a package fails the supply-chain security audit, requires native binaries the sandbox cannot run, depends on Node APIs the runtime does not expose, is deprecated or unmaintained, carries a copyleft license, or matches a malware advisory. Three workable fixes ship the feature: swap to an already-allowlisted alternative package that covers the same capability (fastest path, 1-2 hours), rewrite the feature using vanilla browser APIs like fetch, Intl, structuredClone, and crypto.subtle (most durable, removes the dependency), or request allowlisting through Base44 support with a security justification (5-15 business days). For features that fundamentally need the rejected package, move that surface out of Base44 into a separate microservice you control end-to-end.
You asked the Base44 agent to add a feature. The agent generated code that imported an npm package — say puppeteer, node-cron, pdfkit, or tensorflow-js. The build failed. The editor surfaced a message that read something like:
Dependency 'puppeteer' is not in the Base44 allowlist.
Reason: requires native binaries / chromium download.
Suggested alternatives: react-pdf, jspdf.
The error is not a bug. It is a deliberate guardrail. Base44's sandbox enforces a curated dependency allowlist — the same set of packages are available to every Base44 app, with the same security posture and the same performance profile. The platform team treats the allowlist as a security and stability surface, not as a workaround target.
The good news: most rejections have a clean fix path. The bad news: a small subset of dependencies genuinely cannot be replaced inside Base44, and the right answer for those is to move the feature out of the platform.
What causes the npm package rejection in base44
Five gates fire the rejection error. Knowing which gate fired tells you which fix path to take.
Supply-chain security audit. Base44 runs incoming packages through static analysis against advisory databases (GitHub Advisory Database, npm audit, Snyk, Socket). A package with an open critical or high advisory — or whose transitive dependency tree contains one — is rejected at the gate. The rejection message usually cites the advisory ID.
Sandbox runtime compatibility. Base44's build and execution sandbox does not expose the full Node.js surface area. Packages that need native binaries (puppeteer, sharp, canvas, node-sass), direct file-system access outside the app sandbox (fs-extra patterns), child-process spawning (execa, cross-spawn), or low-level networking (net, dgram, tls beyond fetch) get rejected because the runtime cannot honor their requirements.
Maintenance health. Packages that have not been updated in over two years, are explicitly deprecated on npm, or are flagged as abandoned on openbase.com fail the maintenance check. The platform treats unmaintained code as elevated risk regardless of current vulnerability state.
License compatibility. Base44 redistributes your generated app code as part of the platform's hosting and deployment model. Copyleft licenses (GPL, AGPL, LGPL, and some custom copyleft variants) are typically rejected because they impose obligations the platform's distribution model cannot satisfy. MIT, Apache 2.0, BSD, ISC, and Unlicense pass cleanly.
Bundle-size budget. A small number of packages get rejected on size alone — anything that adds more than a few hundred kilobytes to the client bundle for a single feature trips the budget check. Lodash (full bundle), moment.js, and some chart libraries fall into this category, with the platform recommending tree-shakeable or lighter alternatives.
Sources: Base44 platform documentation on supported dependencies, feedback.base44.com threads on package requests, npm advisory database, Snyk vulnerability database.
How to confirm a base44 npm package rejection (reproduction)
- Open your Base44 app in the editor. Find the route or component where the rejected package was imported.
- Open the build log panel (usually under the "Logs" or "Build" tab on the editor sidebar). Locate the most recent failed build.
- Search the log for "not in the allowlist", "rejected", "dependency error", or the package name. Copy the full error message into a scratch file.
- Note the rejection reason cited in the message. The five common citations are: "security advisory", "native binary required", "sandbox incompatible", "license", and "deprecated".
- Run
npm view <package-name>from your local terminal if you have npm installed. Note the license, last publish date, weekly downloads, and any deprecation flag. - Open the package on socket.dev. Note any flagged supply-chain risks, install scripts, or shell access.
- Check the package's GitHub repository for the date of the last commit and the count of open critical issues. A repo with no commits in 18+ months and 50+ open issues is unlikely to pass an allowlist request even on re-submit.
- Confirm that no alternative package on the same category is already installed in your Base44 app's dependency list. Sometimes a different team member already added a substitute and the build error is a duplicate-imports issue, not a true rejection.
If steps 1-8 all confirm a real rejection, move to the fix paths below.
How to fix base44 npm package rejected error — three workable paths
Pick the path that matches the rejection category and your timeline.
Path A: Swap to an already-allowlisted alternative (fastest, recommended default)
For 70-80 percent of rejection cases, an allowlisted alternative exists that covers the same capability. The swap is usually a 20-60 minute job.
Common swap pairs by category:
DATE/TIME
moment.js (rejected: bundle size, deprecated) → date-fns or dayjs
luxon (sometimes rejected: bundle size) → date-fns or vanilla Intl.DateTimeFormat
HTTP CLIENTS
request (rejected: deprecated) → axios or vanilla fetch
node-fetch (rejected: not needed) → vanilla fetch (already global)
superagent (sometimes rejected) → axios
VALIDATION
joi (sometimes rejected: bundle size) → zod or yup
ajv (rejected: dynamic eval) → zod with JSON schema mode
PDF GENERATION
puppeteer (rejected: native binary) → jspdf or react-pdf (client-side)
pdfkit (sometimes rejected: legacy) → jspdf
CHARTS
chart.js (sometimes rejected: bundle size) → recharts (allowlisted by default)
d3 (sometimes rejected: bundle size) → recharts or visx subset
UTILITY
lodash full (rejected: bundle size) → lodash-es per-method imports or vanilla
underscore (rejected: deprecated) → lodash-es per-method or vanilla
The pattern is consistent: prefer modern, tree-shakeable, MIT-licensed packages over older bundle-heavy or deprecated ones. Most swaps require only an import rewrite and a few API signature changes.
// Before — rejected
import moment from "moment";
const formatted = moment(date).format("MMM D, YYYY");
// After — allowlisted swap
import { format } from "date-fns";
const formatted = format(date, "MMM d, yyyy");
If you cannot find an obvious swap, search openbase.com for the package category — its similarity-ranked alternative list usually surfaces an allowlisted candidate within the top 5.
Path B: Vanilla browser-API rewrite (most durable)
For roughly 15-20 percent of rejection cases, the package can be removed entirely because the Web Platform now ships the capability natively. This is the most durable fix because it removes a dependency rather than swapping one, and vanilla code never gets rejected by an allowlist.
// Before — rejected
import axios from "axios";
const res = await axios.get("/api/orders", { params: { status: "open" } });
return res.data;
// After — vanilla 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 vanilla wins outright:
- HTTP requests —
fetchcovers 95 percent of axios use cases. - Date and number formatting —
Intl.DateTimeFormat,Intl.NumberFormat,Intl.RelativeTimeFormat. - UUIDs —
crypto.randomUUID()is in every modern browser. - Hashing and signing —
crypto.subtlecovers SHA-256, HMAC, AES. - Deep cloning —
structuredClone(obj)replaces lodashcloneDeepfor most JSON-safe data. - Debounce and throttle — 8-12 lines of vanilla code each; lodash's versions add ~70 KB to the bundle for two functions.
The vanilla path adds a few extra lines per call site but eliminates an entire dependency from your supply chain. It also passes the allowlist on the first try every time.
Path C: Request allowlisting from Base44 support (slowest, for genuinely irreplaceable packages)
If the package is critical, has no allowlisted alternative, and cannot be replaced by vanilla code, file a support request. Base44's team reviews allowlist requests within 5-15 business days.
A good allowlist request includes:
- Package name and exact version you want approved.
- Weekly npm download count (screenshot from npmjs.com).
- License type (must be MIT, Apache 2.0, BSD, ISC, or Unlicense for a clean path).
- Last publish date (within the past 12 months strongly preferred).
- A Snyk or Socket security report screenshot showing zero open critical or high advisories.
- A 2-3 sentence justification for why no allowlisted alternative is acceptable for your use case.
- A link to the package's GitHub repo and primary maintainer.
Email: support@base44.com. Mention "package allowlist request" in the subject.
Realistic expectations: well-maintained MIT packages with over 1 million weekly downloads usually get approved within 5-7 business days. Single-maintainer packages, packages under 500k weekly downloads, or packages with any flagged supply-chain risk often get rejected on the first pass with a request for more information or a suggested alternative.
While the request is pending, do not block your sprint. Ship a Path A or Path B fix immediately so the feature ships. When the allowlist request lands, you can decide whether to migrate to the now-allowed package or keep the working swap.
Path D: Move the feature to a separate microservice (escape valve for fundamental conflicts)
If the rejected package fundamentally cannot run in a browser sandbox — chromium-backed PDF rendering, heavy ML inference, an enterprise SDK that needs Node — the right answer is architectural. Provision a small server-side service on Vercel, Cloudflare Workers, Railway, or Fly.io, host the rejected package there, and expose a clean HTTPS endpoint that your Base44 app calls.
// Inside your Base44 app — call the external microservice
async function generatePdf(payload: PdfPayload): Promise<string> {
const res = await fetch("https://pdf.your-microservice.com/render", {
method: "POST",
headers: { "content-type": "application/json", authorization: `Bearer ${TOKEN}` },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`PDF generation failed: ${res.status}`);
const { url } = await res.json();
return url;
}
The external service handles the puppeteer or other restricted dependency. Your Base44 app handles the user-facing UI and calls into the service over HTTPS. This pattern is the standard escape hatch for any platform-level dependency restriction and works cleanly with Base44's networking model.
The microservice path adds operational cost (a second deploy target, monitoring, scaling) but unlocks any dependency. It is the correct answer for genuinely irreplaceable packages and the wrong answer for routine swaps that could ship with Path A in an hour.
In all paths: pin and document your dependency choice
Once you ship a fix, pin the alternative package to an exact version in your Base44 app's dependency list (no caret-range surprises) and add a short comment in the code explaining why the rejected package was avoided. A future agent prompt or human maintainer will otherwise try to re-introduce the rejected import and re-hit the same error.
// Note: moment was rejected by Base44 allowlist (bundle size + deprecated).
// Using date-fns instead — do not swap back without an allowlist check.
import { format } from "date-fns";
How long does it take to fix base44 npm package rejection?
Timeline depends on which path applies.
- Path A (swap to allowlisted alternative): 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 (vanilla browser-API rewrite): 1-3 hours depending on how many call sites used the rejected package. Add 1-2 hours for testing. Vanilla rewrites are slower than swaps but more durable.
- Path C (allowlist request from Base44 support): 5-15 business days waiting for review, with the Path A or Path B fix shipped immediately so you are not blocked. If the allowlist approval lands, migration back to the requested package takes another 1-2 hours.
- Path D (microservice for fundamental conflicts): 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 microservice; 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. Do not block a sprint waiting for a support response.
DIY vs hire decision
DIY this if: The rejected package falls in a category with obvious swaps (dates, HTTP, validation, charts, utility), you are comfortable rewriting against a slightly different API, and you have basic testing coverage. Path A swaps are reliably DIY-friendly. Vanilla rewrites (Path B) are DIY if you are familiar with modern Web Platform APIs.
Hire help if: The rejected package is central to multiple screens and signature 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 (microservice) and you do not have experience standing up production services. A fix-sprint engagement typically covers a single rejected-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 rejected-package fix, our fix-sprint engagement covers the swap, vanilla rewrite, or microservice setup in a fixed-price block. We handle the dependency audit, the alternative selection, the implementation, and the regression testing. For larger dependency-allowlist conflicts (5+ rejected packages, microservice architecture, recurring allowlist friction across a team), the complex-fix engagement plans a portfolio-level dependency strategy.
Start a fix-sprint engagement for an npm rejection fix
Related problems
- Vendor lock-in via SDK dependency — the broader pattern that allowlist friction is one symptom of, 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 — the upstream cause of why the agent sometimes re-imports a previously-rejected package after a long session.