{"record":{"id":"451a8b4b548611df","repo":"NousResearch/hermes-agent","slug":"default-export-must-be-register-sdk","errorCode":null,"errorMessage":"default export must be register(sdk)","messagePattern":"default export must be register\\(sdk\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ui-tui/src/sdk/userWidgets.ts","lineNumber":122,"sourceCode":"\n      for (const id of ids) {\n        if (removeWidgetApp(id)) {\n          result.removed.push(id)\n        }\n      }\n    }\n  }\n\n  for (const file of files) {\n    const before = new Set(listWidgetApps().map(app => app.id))\n\n    try {\n      const mod = (await import(`${pathToFileURL(join(dir, file)).href}?t=${Date.now()}`)) as {\n        default?: (sdk: WidgetSdk) => void\n      }\n\n      if (typeof mod.default !== 'function') {\n        throw new Error('default export must be register(sdk)')\n      }\n\n      mod.default(widgetSdk)\n      result.loaded.push(file)\n\n      const ids = listWidgetApps()\n        .map(app => app.id)\n        .filter(id => !before.has(id))\n\n      // Re-registrations of existing ids keep their prior file attribution.\n      if (ids.length) {\n        fileApps.set(file, ids)\n        result.added.push(...ids)\n      }\n    } catch (error) {\n      const message = error instanceof Error ? error.message : String(error)\n\n      result.errors.push({ file, message })","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/ui-tui/src/sdk/userWidgets.ts#L104-L140","documentation":"The dashboard/TUI user-widgets loader dynamically imports every JS file in the user widgets directory and calls its default export with the WidgetSdk instance. This error means the imported module's default export is missing or not a function, so the loader cannot register any widgets from it. It is thrown per-file during discovery, before mod.default(widgetSdk) runs.","triggerScenarios":"A file in the widgets directory (a) has no `export default`, (b) exports a default object/const instead of a function, or (c) uses `export default register` where register is undefined at module-evaluation time. Triggered by `await import(pathToFileURL(...)?t=...)` followed by `typeof mod.default !== 'function'`.","commonSituations":"Authoring a widget as `export const register = (sdk) => {}` (named instead of default), exporting a configuration object, or a half-written/placeholder widget file dropped into the widgets dir. Also CommonJS modules or transpiled bundles whose default export lands under `module.exports.default`.","solutions":["Change the widget file to `export default (sdk) => { sdk.registerWidget(...) }` — a default-exported function taking the sdk.","If the file is not meant to be a widget (e.g. a shared helper), move it out of the scanned widgets directory or give it an extension the loader ignores.","If bundling the widget, verify the bundle retains a function default export (`output.libraryExport: 'default'` or equivalent)."],"exampleFix":"// before\nexport const register = (sdk) => {\n  sdk.registerWidget({ id: 'clock' })\n}\n\n// after\nexport default (sdk) => {\n  sdk.registerWidget({ id: 'clock' })\n}","handlingStrategy":"type-guard","validationCode":"const mod: { default?: unknown } = await import(url)\nconst isRegister = (v: unknown): v is (sdk: WidgetSdk) => void =>\n  typeof v === 'function'\nif (!isRegister(mod.default)) {\n  console.warn(`skipping ${file}: no default register(sdk) export`)\n  continue\n}","typeGuard":"function isRegisterExport(mod: unknown): mod is { default: (sdk: WidgetSdk) => void } {\n  return typeof (mod as { default?: unknown })?.default === 'function'\n}","tryCatchPattern":"// loader already wraps each file; keep per-file isolation so one bad widget never aborts discovery\ntry {\n  const mod = await import(url)\n  if (typeof mod.default !== 'function') throw new Error('default export must be register(sdk)')\n  mod.default(widgetSdk)\n} catch (err) {\n  result.failed.push({ file, error: String(err) }) // skip file, continue loop\n}","preventionTips":["Codify the widget contract: default-export a function taking sdk.","Keep non-widget helper files out of the scanned directory.","Log the failing filename in the error so authors can locate the bad file."],"tags":["widgets","dynamic-import","es-modules","dashboard"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}