# Import errors with esm.sh or npm packages

> Fix module-not-found, bare specifier and esm.sh errors in SupaHost Edge Functions. Use npm: and jsr: specifiers, pin versions, and know which packages can't run in Deno.

Source: https://supahost.app/kb/esm-sh-imports · Updated 2026-11-11

## What you see

A deploy ends in `error`, or the function fails to boot with `503 BOOT_ERROR`, and the logs or a local `deno check` show something like:

```text
error: Relative import path "stripe" not prefixed with / or ./ or ../
```

```text
error: Module not found "https://esm.sh/some-lib@1.2.3/dist/index.js"
```

Or the import works, but the package throws at runtime because it expects a browser or a Node API that isn't there.

## Why it happens

Edge Functions run on Deno, which resolves imports by explicit specifier. A bare name such as `'stripe'` means nothing unless something maps it to a package. Functions on SupaHost support three kinds of remote import:

| Specifier | Example | Notes |
|---|---|---|
| `npm:` | `npm:stripe@17.5.0` | Deno's built-in npm support. Usually the best choice. |
| `jsr:` | `jsr:@std/encoding@1` | Packages from the JSR registry. |
| `https:` | `https://esm.sh/nanoid@5.0.9` | Any URL that serves ES modules, including CDNs like esm.sh. |

esm.sh converts npm packages on the fly. That's convenient, but the output can change between builds, some packages convert badly, and an outage on the CDN becomes an outage for your deploy.

## How to fix it

1. **Prefix every package.** Replace bare names with `npm:` and a version:

   ```ts
   // before
   import Stripe from 'stripe'
   // after
   import Stripe from 'npm:stripe@17.5.0'
   ```

2. **Prefer `npm:` over esm.sh** for packages on npm. Deno installs the real package, so you avoid conversion problems:

   ```ts
   import { createClient } from 'npm:@supabase/supabase-js@2'
   ```

3. **Pin versions.** `npm:zod@3.24.1` always resolves to the same code. An unpinned import can pick up a breaking release the next time you deploy.
4. **Use `node:` for Node built-ins.** Libraries that need `Buffer` or `crypto` from Node work through Deno's compatibility layer; in your own code import them explicitly, for example `import { Buffer } from 'node:buffer'`.
5. **Write full specifiers in the code itself.** Don't depend on a `deno.json` or import map to turn bare names into packages; put the full `npm:`, `jsr:` or `https:` specifier in each `import` line.
6. **Avoid packages that can't run here.** Native add-ons (packages that compile C or C++ code, such as `sharp` or `bcrypt`), packages that need a real filesystem, and ones that start child processes won't work. Look for a pure JavaScript or WebAssembly alternative, such as `bcryptjs` instead of `bcrypt`.
7. **Check locally before deploying:**

   ```bash
   deno check index.ts
   ```

## On SupaHost

The deploy step bundles your function on the instance and resolves every import, so a bad import shows up as a deploy `error` and the previous version keeps serving. The bundle also warms the module cache, so the first request after a deploy doesn't have to download dependencies. Relative imports between your own files work as usual (`./_shared/cors.ts`); a function can have up to 50 files and 1 MB of source in total, counted over the files you upload.

## Related

- [Writing functions: Deno, imports and structure](/docs/functions/writing-functions)
- [Edge Function deploy failed](/kb/function-deploy-failed)
- [Edge Function returns 503 or 504](/kb/function-503-504)
- [Edge Function limits](/docs/functions/limits)
