LearnNewsExamplesServices
Frontmatter
id13193
titlecreate_component: reject non-class `module` — a string `module` crashes container.add (createItem)
stateClosed
labels
bugai
assigneesneo-opus-vega
createdAtJun 14, 2026, 9:03 AM
updatedAtJun 14, 2026, 10:06 AM
githubUrlhttps://github.com/neomjs/neo/issues/13193
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 14, 2026, 10:06 AM

create_component: reject non-class module — a string module crashes container.add (createItem)

neo-opus-vega
neo-opus-vega commented on Jun 14, 2026, 9:03 AM

Summary

create_component's config validation accepts module, but module is unusable over the Neural Link wire and a STRING module crashes the downstream container.add. The tool should reject module-as-non-class (guide agents to ntype/className) with a clear semantic error, instead of letting a cryptic runtime error surface from deep in the container.

Repro

Call create_component with config: { module: 'Neo.button.Base', text: 'X' } (a natural thing for an agent to try — module is the most discoverable field). Result:

TypeError: Cannot read properties of undefined (reading 'className')
    at MainContainer.createItem (src/container/Base.mjs:445)
    at ...insert → add → InstanceService.callMethod → Client.handleRequest

Root Cause

ComponentService.createComponent delegates to call_method(parentId, 'add', [config])container.add(config)createItem (src/container/Base.mjs:440-445):

module = item.module;                                  // = the STRING 'Neo.button.Base'
lazyLoadItem = module && !module.isClass && Neo.isFunction(module);   // false for a string
if (module && !lazyLoadItem) {
    item.className = module.prototype.className;       // 'string'.prototype is undefined → crash
}

Over the wire, module can be neither a serializable class (classes don't serialize) nor a valid string (breaks createItem). So module is effectively dead for NL — only ntype / className (resolvable strings) work.

Proposed Fix

In ComponentService.createComponent validation: if config.module is present and not a class, reject with a semantic error pointing to ntype/className (e.g. "module is not usable over the Neural Link wire; declare ntype or className instead"). Keep ntype/className as the sanctioned forms. (Alternative: harden createItem to resolve a string module via Neo.ns, but the NL-tool-level reject is the tighter fix — fail-fast at the boundary.)

Acceptance Criteria

  • create_component with a non-class module is rejected server-side with a clear semantic error (no dispatch, no cryptic downstream crash).
  • ntype / className configs continue to work (regression coverage).
  • Unit coverage for the reject + the sanctioned forms.

Context

Found while building the multi-window create_component e2e (#13185 / PR #13191): the first config used module: 'Neo.button.Base' and crashed; switching to ntype: 'button' fixed it. Refs #9846 (create_component), #13185.

Authored by @neo-opus-vega (Vega, Claude Opus 4.8) — origin session a0bc6b23-78c5-4bd7-b944-9db5e236f42d.