{"record":{"id":"d67e518f7eb4786e","repo":"NousResearch/hermes-agent","slug":"unsupported-import-unsupported-length-1-s","errorCode":null,"errorMessage":"unsupported import${unsupported.length > 1 ? 's' : ''}: ${unsupported.join(', ')} — runtime plugins may only import @hermes/plugin-sdk and react","messagePattern":"unsupported import(.+?): (.+?) — runtime plugins may only import @hermes/plugin-sdk and react","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/src/contrib/runtime-loader.ts","lineNumber":123,"sourceCode":"}\n\n/** Evaluate + register one runtime plugin. Returns its id, or null on failure. */\nexport async function loadRuntimePlugin(\n  source: string,\n  origin: string,\n  options: LoadOptions = {}\n): Promise<null | string> {\n  installPluginSdk()\n\n  try {\n    if (options.integrity && !(await verifyIntegrity(source, options.integrity))) {\n      throw new Error(`integrity check failed for ${origin}`)\n    }\n\n    const unsupported = unsupportedImports(source)\n\n    if (unsupported.length > 0) {\n      throw new Error(\n        `unsupported import${unsupported.length > 1 ? 's' : ''}: ${unsupported.join(', ')} — ` +\n          `runtime plugins may only import @hermes/plugin-sdk and react`\n      )\n    }\n\n    const url = URL.createObjectURL(new Blob([rewriteSpecifiers(source)], { type: 'text/javascript' }))\n\n    let mod: { default?: HermesPlugin }\n\n    try {\n      mod = await import(/* @vite-ignore */ url)\n    } finally {\n      URL.revokeObjectURL(url)\n    }\n\n    const plugin = mod.default\n\n    if (!plugin?.id || typeof plugin.register !== 'function') {","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/src/contrib/runtime-loader.ts#L105-L141","documentation":"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().","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\nimport { debounce } from 'lodash-es'\nimport { useEffect } from 'react'\n\n// after\nimport { useEffect } from 'react'\nfunction debounce(fn: (...a: unknown[]) => void, ms: number) { /* inlined */ }","handlingStrategy":"validation","validationCode":"const ALLOWED_BARE = new Set(['@hermes/plugin-sdk'])\nfunction badImports(source: string): string[] {\n  const re = /(from\\s*|import\\s*\\(\\s*|import\\s+)(['\"])([^'\"]+)\\2/g\n  const bad = new Set<string>()\n  for (const m of source.matchAll(re)) {\n    const spec = m[3]!\n    if (!/^[./]/.test(spec) && !/^[a-z][a-z0-9+.-]*:/i.test(spec) && !ALLOWED_BARE.has(spec) && !/^react/.test(spec)) bad.add(spec)\n  }\n  return [...bad]\n}\nif (badImports(pluginSource).length) rejectPlugin('inline or drop: ' + badImports(pluginSource).join(', '))","typeGuard":null,"tryCatchPattern":"try { await loadRuntimePlugin(src, origin) } catch (e) { if (e instanceof Error && e.message.startsWith('unsupported import')) { /* list the named specifiers, inline them, retry */ } }","preventionTips":["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"],"tags":["desktop","plugins","esm","imports","validation"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}