anomalyco / anomalyco/opencode

`import.meta.resolve(dir)` one-arg form returns directory URL on Node.js, causing custom npm providers to fail with `ERR_UNSUPPORTED_DIR_IMPORT`

Open
#46,141 1 comment 0 reactions 1 assignee View on GitHub

@rekram1-node is already working on this.

Since Aug 29, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Summary

Custom npm providers configured via the npm field in opencode.jsonc fail to initialize in OpenCode's TUI/server mode when running on Node.js, while the same configuration works in the CLI (Bun). The root cause is an asymmetry in import.meta.resolve() between Bun and Node.js in packages/core/src/npm.ts.

Environment

  • OpenCode: v1.18.25
  • Runtime: Node.js v24.14.0 (also affects Electron/Desktop)
  • OS: Windows 10

Symptom

When a custom provider is configured via npm package name:

{
  "plugin": ["opencode-cmd-provider"],
  "provider": {
    "commandcode": {
      "npm": "opencode-cmd-provider",
      "name": "Command Code",
      "options": {
        "apiKey": "..."
      }
    }
  }
}
  • opencode models commandcode — works ✅
  • opencode run -m commandcode/deepseek/deepseek-v4-flash "hello" — works ✅
  • Interactive TUI — fails with Failed to initialize provider: commandcode

Using file:// URL directly to the dist entry file fixes all modes:

"npm": "file:///absolute/path/to/opencode-cmd-provider/dist/index.js"

Root Cause

In packages/core/src/npm.ts, the resolveEntryPoint function handles Bun and Node.js differently:

const resolveEntryPoint = (name: string, dir: string): EntryPoint => {
  let entrypoint: Option.Option<string>
  try {
    const resolved = typeof Bun !== "undefined"
      ? import.meta.resolve(name, dir)   // ✅ Bun: resolves to entry file
      : import.meta.resolve(dir)          // ❌ Node: returns directory URL
    entrypoint = Option.some(resolved)
  } catch {
    entrypoint = Option.none()
  }
  return { directory: dir, entrypoint }
}
  • Bun path: import.meta.resolve(name, dir) — correctly resolves the package name relative to the directory, returning the actual entry file (e.g., file:///.../dist/index.js)
  • Node path: import.meta.resolve(dir) — merely converts the directory path to a file:// URL

The caller then does await import(importSpec) with the resolved path. Node.js ESM does not support import() of a directory URL — it throws ERR_UNSUPPORTED_DIR_IMPORT regardless of what package.json declares in main/exports.

This explains why:

  • CLI commands (run, models) work — they may use a cached/loaded SDK or a different resolution path
  • TUI/server fails — it freshly initializes providers via Npm.add() and hits the directory import error
  • file:// works — it bypasses npm resolution entirely, passing the exact file path to import()

Fix

Both Bun and Node.js 20.6+ support the two-argument form of import.meta.resolve(name, dir). The fix is to use it consistently:

const resolveEntryPoint = (name: string, dir: string): EntryPoint => {
  let entrypoint: Option.Option<string>
  try {
-   const resolved = typeof Bun !== "undefined"
-     ? import.meta.resolve(name, dir)
-     : import.meta.resolve(dir)
+   const resolved = import.meta.resolve(name, dir)
    entrypoint = Option.some(resolved)
  } catch {
    entrypoint = Option.none()
  }
  return { directory: dir, entrypoint }
}

Related

  • PR #44722 — Alternative approach that resolves entry from manifest (exportsmodulemainindex.js) instead of fixing the import.meta.resolve call
  • PR #32060 — Earlier attempt (auto-closed by stale bot)
  • Issue #31909 — Original bug report for Desktop/Electron
  • Issue rashidrazak/opencode-cmd-provider#102 — Downstream plugin report documenting the file:// workaround

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.