codex-team/editor.js · critical · Error
Can't start without tools
Error message
Can't start without tools
What it means
Editor.js throws this during Editor initialization (prepare) when, after merging internal tools with user-provided config.tools, the resulting tools object is empty. The library refuses to start because an editor with no tools would be unusable. It also fires when the tools key is missing entirely from the config object.
Source
Thrown at src/components/modules/tools.ts:117
public get internal(): ToolsCollection {
return this.available.internalTools;
}
/**
* Creates instances via passed or default configuration
*
* @returns {Promise<void>}
*/
public async prepare(): Promise<void> {
this.validateTools();
/**
* Assign internal tools
*/
this.config.tools = _.deepMerge({}, this.internalTools, this.config.tools);
if (!Object.prototype.hasOwnProperty.call(this.config, 'tools') || Object.keys(this.config.tools).length === 0) {
throw Error('Can\'t start without tools');
}
const config = this.prepareConfig();
this.factory = new ToolsFactory(config, this.config, this.Editor.API);
/**
* getting classes that has prepare method
*/
const sequenceData = this.getListOfPrepareFunctions(config);
/**
* if sequence data contains nothing then resolve current chain and run other module prepare
*/
if (sequenceData.length === 0) {
return Promise.resolve();
}
View on GitHub (pinned to 5f45dabbe5)
Solutions
- Add a non-empty tools object to the Editor.js config: tools: { paragraph: Paragraph } (import @editorjs/paragraph or another tool).
- If tools are loaded asynchronously, await them before constructing new EditorJS(...); construct the editor only after tools resolve.
- Debug-log Object.keys(config.tools || {}) right before initialization to confirm the object is populated.
- Never pass tools: {} or undefined; at minimum register the Paragraph tool, which Editor.js expects for block editing.
Example fix
// before
const editor = new EditorJS({ holder: 'editor-js' });
// after
import Paragraph from '@editorjs/paragraph';
const editor = new EditorJS({
holder: 'editor-js',
tools: {
paragraph: Paragraph
}
}); Defensive patterns
Strategy: validation
Validate before calling
import Paragraph from '@editorjs/paragraph';
function assertToolsReady(config) {
if (!config.tools || Object.keys(config.tools).length === 0) {
throw new Error('Editor.js config.tools is empty — register at least one tool (e.g. paragraph) before init.');
}
}
// before new EditorJS(...):
assertToolsReady(config); Type guard
function hasValidTools(config) {
return Object.prototype.hasOwnProperty.call(config, 'tools')
&& config.tools !== null
&& typeof config.tools === 'object'
&& Object.keys(config.tools).length > 0;
} Prevention
- Always include at least the Paragraph tool in every Editor.js config.
- When tools load asynchronously, await them before constructing the editor.
- Type your config as { tools: Record<string, unknown> } and fail fast on empty objects in dev builds.
When it happens
Trigger: Calling new EditorJS({...}) without a tools property, or passing tools: {} (empty object). It can also occur if tools is passed as undefined/null or a non-plain object whose keys don't survive Object.keys(), since the merge of internalTools with user tools yields nothing usable.
Common situations: Most commonly a developer copies a minimal example and forgets the tools block, or builds config dynamically (e.g. tools: loadedTools where loadedTools is an empty object before an async fetch resolves). Also happens when config is spread from another object that omits tools, or after upgrading from examples that assumed default tools exist.
Related errors
- Tool «${toolName}» must be a constructor function or an obje
- Unable to move Block down since it is already the last
- Unable to move Block up since it is already the first
- Incorrect data passed to the render() method
- Block with id "${id}" not found
AI-assisted analysis of codex-team/editor.js@5f45dabbe5 (2026-08-27).
Data as JSON: /api/errors/bcc195cd06a33abd.
Report an issue: GitHub.