markedjs/marked · error · Error

extension level must be 'block' or 'inline'

Error message

extension level must be 'block' or 'inline'

What it means

Thrown by Marked.use() when an extension that declares a tokenizer ('tokenizer' in ext is true) has a level that is missing, empty, or not exactly 'block' or 'inline'. The level tells marked where to inject the tokenizer: block tokenizers run in Lexer.blockTokens and inline tokenizers run in Lexer.inlineTokens (Lexer.ts:124-131, 353-360). Without a valid level marked cannot route the tokenizer, so registration is aborted. Renderer-only extensions (no tokenizer) do not need a level and never trigger this.

Source

Thrown at src/Instance.ts:131

          }
          if ('renderer' in ext) { // Renderer extensions
            const prevRenderer = extensions.renderers[ext.name];
            if (prevRenderer) {
              // Replace extension with func to run new extension but fall back if false
              extensions.renderers[ext.name] = function(...args) {
                let ret = ext.renderer.apply(this, args);
                if (ret === false) {
                  ret = prevRenderer.apply(this, args);
                }
                return ret;
              };
            } else {
              extensions.renderers[ext.name] = ext.renderer;
            }
          }
          if ('tokenizer' in ext) { // Tokenizer Extensions
            if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
              throw new Error("extension level must be 'block' or 'inline'");
            }
            const extLevel = extensions[ext.level];
            if (extLevel) {
              extLevel.unshift(ext.tokenizer);
            } else {
              extensions[ext.level] = [ext.tokenizer];
            }
            if (ext.start) { // Function to check for start of token
              if (ext.level === 'block') {
                if (extensions.startBlock) {
                  extensions.startBlock.push(ext.start);
                } else {
                  extensions.startBlock = [ext.start];
                }
              } else if (ext.level === 'inline') {
                if (extensions.startInline) {
                  extensions.startInline.push(ext.start);
                } else {

View on GitHub (pinned to 9552b6bbca)

Solutions

  1. Add level:'block' or level:'inline' (lowercase) to the extension object.
  2. If you only want to change rendering of existing tokens, drop the tokenizer and keep renderer plus name (no level needed).
  3. When building extensions generically, default-fill level from a constant before calling use().

Example fix

// before
marked.use({
  extensions: [{
    name: 'mention',
    tokenizer(src) { const m = /^@(\w+)/.exec(src); if (!m) return; return { type:'mention', raw:m[0], text:m[1], tokens:[] }; }
  }]
});

// after
marked.use({
  extensions: [{
    name: 'mention',
    level: 'inline',
    tokenizer(src) { const m = /^@(\w+)/.exec(src); if (!m) return; return { type:'mention', raw:m[0], text:m[1], tokens:[] }; }
  }]
});
Defensive patterns

Strategy: validation

Validate before calling

for (const ext of myExt.extensions ?? []) {
  if ('tokenizer' in ext && ext.level !== 'block' && ext.level !== 'inline') {
    throw new Error('extension "' + (ext.name ?? '<unnamed>') + '" needs level: \'block\' | \'inline\'');
  }
}
marked.use(myExt);

Type guard

function hasValidLevel(ext) {
  return !('tokenizer' in ext) || ext.level === 'block' || ext.level === 'inline';
}

Prevention

When it happens

Trigger: marked.use({ extensions:[{ name:'x', tokenizer(src){...} }] }) with no level; level:'Block' or 'block-level' (wrong casing/wording, must be lowercase exactly); level:'' or level:undefined; spreading a base extension that had level but overriding it to an invalid value.

Common situations: Copying a renderer-extension example and adding a tokenizer without realising level becomes mandatory; assuming the default level is block (there is no default); writing 'Block' for readability; case-sensitivity mistakes.

Related errors


AI-assisted analysis of markedjs/marked@9552b6bbca (2026-08-13). Data as JSON: /api/errors/660c14037d5f5f7a. Report an issue: GitHub.