codex-team/editor.js · error · Error
Tool «${toolName}» must be a constructor function or an obje
Error message
Tool «${toolName}» must be a constructor function or an object with function in the «class» property What it means
During prepare, Editor.js validates every entry in config.tools. Each entry must be either a class/constructor function (the tool implementation) or a plain settings object containing a class property that is a function. If neither condition holds, this error is thrown naming the offending tool key.
Source
Thrown at src/components/modules/tools.ts:392
}
/**
* Validate Tools configuration objects and throw Error for user if it is invalid
*/
private validateTools(): void {
/**
* Check Tools for a class containing
*/
for (const toolName in this.config.tools) {
if (Object.prototype.hasOwnProperty.call(this.config.tools, toolName)) {
if (toolName in this.internalTools) {
return;
}
const tool = this.config.tools[toolName];
if (!_.isFunction(tool) && !_.isFunction((tool as ToolSettings).class)) {
throw Error(
`Tool «${toolName}» must be a constructor function or an object with function in the «class» property`
);
}
}
}
}
/**
* Unify tools config
*/
private prepareConfig(): {[name: string]: ToolSettings} {
const config: {[name: string]: ToolSettings} = {};
/**
* Save Tools settings to a map
*/
for (const toolName in this.config.tools) {
/**View on GitHub (pinned to 5f45dabbe5)
Solutions
- Check the import style for the offending tool: most Editor.js tools use a default export — use import Header from '@editorjs/header'; verify with the tool's package README.
- If using a settings object, include the class key pointing at the constructor: { header: { class: Header, inlineToolbar: true } }.
- Log the value (console.log(typeof Header, Header)) before initialization to spot undefined/string values caused by bad imports or circular dependencies.
- If lazy-loading tools, ensure the module has fully evaluated (await import(...)) before passing it into tools.
Example fix
// before
import { Header } from '@editorjs/header'; // wrong: named import is undefined
const editor = new EditorJS({
holder: 'editor-js',
tools: { header: { class: Header, inlineToolbar: true } }
});
// after
import Header from '@editorjs/header'; // default export is the constructor
const editor = new EditorJS({
holder: 'editor-js',
tools: { header: { class: Header, inlineToolbar: true } }
}); Defensive patterns
Strategy: type-guard
Validate before calling
function assertToolsAreConstructors(tools) {
for (const [name, entry] of Object.entries(tools)) {
const candidate = typeof entry === 'object' && entry !== null ? entry.class : entry;
if (typeof candidate !== 'function') {
throw new Error(`Tool "${name}" is not a constructor (got ${String(candidate)}). Check its import.`);
}
}
}
assertToolsAreConstructors(config.tools); // before new EditorJS(...) Type guard
function isToolConfig(entry) {
if (typeof entry === 'function') return true;
return typeof entry === 'object'
&& entry !== null
&& typeof entry.class === 'function';
}
// usage: Object.values(config.tools).every(isToolConfig) Try / catch
try {
const editor = new EditorJS(config);
} catch (e) {
if (e instanceof Error && e.message.includes('must be a constructor function')) {
console.error('Bad tool registration:', e.message);
// fix imports / re-check config.tools entries
} else {
throw e;
}
} Prevention
- Verify default vs named import style for every tool package before wiring it in.
- Log typeof for each tool entry during development to catch undefined imports early.
- Run a loop over config.tools with a type guard before initialization.
When it happens
Trigger: Passing tools: { header: HeaderTool } where HeaderTool is undefined (bad import), a module namespace object, an instance instead of a class, or a string. Also passing a settings object like { header: { inlineToolbar: true } } without the required class property, or with class set to a non-function value.
Common situations: Very common with broken ES-module imports: named vs default export mismatches (import { Header } from '@editorjs/header' instead of import Header from '@editorjs/header'), circular imports that yield undefined at evaluation time, tree-shaking removing the class, or lazy-loaded tool bundles that haven't evaluated when the editor is constructed. Also occurs from typos: tools: { heaer: Header } works but the tool internals fail elsewhere, whereas tools: { header: 'Header' } (string) throws here.
Related errors
- Incorrect data passed to the render() method
- «holderId» and «holder» param can't assign at the same time.
- Can't start without tools
- Unable to move Block down since it is already the last
- Unable to move Block up since it is already the first
AI-assisted analysis of codex-team/editor.js@5f45dabbe5 (2026-08-27).
Data as JSON: /api/errors/3bfc507396cc091f.
Report an issue: GitHub.