NousResearch/hermes-agent · error
unsupported import${unsupported.length > 1 ? 's' : ''}: ${un
Error message
unsupported import${unsupported.length > 1 ? 's' : ''}: ${unsupported.join(', ')} — runtime plugins may only import @hermes/plugin-sdk and react What it means
Thrown by the runtime-plugin loader (runtime-loader.ts:123) when the plugin source imports bare specifiers that are not `@hermes/plugin-sdk` or `react*`. The loader rewrites only the SDK shim imports to blob URLs before dynamic import(); any other bare specifier (npm package name) cannot resolve inside a blob module, so the loader fails fast with a readable list instead of the cryptic native 'Failed to resolve module specifier' error. Relative imports (./ ../), absolute paths, and URL-scheme imports are allowed through by unsupportedImports().
Source
Thrown at apps/desktop/src/contrib/runtime-loader.ts:123
}
/** Evaluate + register one runtime plugin. Returns its id, or null on failure. */
export async function loadRuntimePlugin(
source: string,
origin: string,
options: LoadOptions = {}
): Promise<null | string> {
installPluginSdk()
try {
if (options.integrity && !(await verifyIntegrity(source, options.integrity))) {
throw new Error(`integrity check failed for ${origin}`)
}
const unsupported = unsupportedImports(source)
if (unsupported.length > 0) {
throw new Error(
`unsupported import${unsupported.length > 1 ? 's' : ''}: ${unsupported.join(', ')} — ` +
`runtime plugins may only import @hermes/plugin-sdk and react`
)
}
const url = URL.createObjectURL(new Blob([rewriteSpecifiers(source)], { type: 'text/javascript' }))
let mod: { default?: HermesPlugin }
try {
mod = await import(/* @vite-ignore */ url)
} finally {
URL.revokeObjectURL(url)
}
const plugin = mod.default
if (!plugin?.id || typeof plugin.register !== 'function') {View on GitHub (pinned to c896c09c42)
Solutions
- Remove the bare import and inline the needed helper code into plugin.js, or reimplement it via `@hermes/plugin-sdk` and `react` only.
- If the dependency is pure data or small, vendor it as a relative file (`./dep.js`) next to plugin.js and import it relatively.
- If it must be a package, convert it into part of the SDK surface (@hermes/plugin-sdk) so the loader maps it — a core change, not a plugin change.
- Re-read the error's list: every named specifier must go, the error enumerates all offenders at once.
Example fix
// before
import { debounce } from 'lodash-es'
import { useEffect } from 'react'
// after
import { useEffect } from 'react'
function debounce(fn: (...a: unknown[]) => void, ms: number) { /* inlined */ } Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED_BARE = new Set(['@hermes/plugin-sdk'])
function badImports(source: string): string[] {
const re = /(from\s*|import\s*\(\s*|import\s+)(['"])([^'"]+)\2/g
const bad = new Set<string>()
for (const m of source.matchAll(re)) {
const spec = m[3]!
if (!/^[./]/.test(spec) && !/^[a-z][a-z0-9+.-]*:/i.test(spec) && !ALLOWED_BARE.has(spec) && !/^react/.test(spec)) bad.add(spec)
}
return [...bad]
}
if (badImports(pluginSource).length) rejectPlugin('inline or drop: ' + badImports(pluginSource).join(', ')) Try / catch
try { await loadRuntimePlugin(src, origin) } catch (e) { if (e instanceof Error && e.message.startsWith('unsupported import')) { /* list the named specifiers, inline them, retry */ } } Prevention
- Author plugins with only @hermes/plugin-sdk and react imports from the start
- Vendor helper code as relative files next to plugin.js
- Run the import lint above on generated plugin source before the agent saves it
- Never assume npm specifiers resolve inside the blob-based runtime loader
When it happens
Trigger: A plugin.js containing `import lodash from 'lodash'`, `import { foo } from '@hermes/other-thing'`, or a side-effect `import 'some-polyfill'`; a dynamic `import('uuid')` inside the plugin; any bare npm-style specifier not present in sdkImportMap().
Common situations: Porting an npm module or a bundled React component into a desktop plugin without inlining dependencies; the agent generating a plugin that pulls in a utility library by habit; version drift where a previously-mapped specifier is removed from the SDK shim map.
Related errors
- ${origin} has no valid default HermesPlugin export
- Not a directory: ${watchDir}
- Branch name is required.
- Missing URL
- Invalid data URL
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/d67e518f7eb4786e.
Report an issue: GitHub.