github/markup · error · ArgumentError

unknown commonmarker extension: #{ext.inspect}

Error message

unknown commonmarker extension: #{ext.inspect}

What it means

The Markdown implementation raises ArgumentError "unknown commonmarker extension: #{ext.inspect}" when options[:commonmarker_exts] contains anything outside the supported extension list: :strikethrough, :tagfilter, :autolink, :table, :tasklist, :shortcodes, :footnotes, :multiline_block_quotes, :math_dollars, :math_code, :wikilinks_title_after_pipe, :wikilinks_title_before_pipe, :underline, :subscript, :spoiler, :greentext, :alerts, :description_lists, and :header_ids (converted to an empty prefix). Unlisted extensions are rejected rather than silently ignored, and note the wrapper explicitly disables defaults (:strikethrough, :tagfilter, :autolink, :table, :tasklist, :shortcodes) unless requested.

Source

Thrown at lib/github/markup/markdown.rb:67

            else
              raise ArgumentError, "unknown commonmarker option: #{opt.inspect}"
            end
          end

          legacy_exts.each do |ext|
            case ext
            when :strikethrough, :tagfilter, :autolink, :table, :tasklist,
                 :shortcodes, :footnotes, :multiline_block_quotes,
                 :math_dollars, :math_code, :wikilinks_title_after_pipe,
                 :wikilinks_title_before_pipe, :underline, :subscript, :spoiler,
                 :greentext, :alerts, :description_lists
              extension_options[ext] = true
            when :header_ids
              # header_ids takes a string prefix in 2.x rather than a boolean. The legacy contract
              # only passed it as a symbol, so use an empty prefix to enable anchor generation.
              extension_options[:header_ids] = ""
            else
              raise ArgumentError, "unknown commonmarker extension: #{ext.inspect}"
            end
          end

          # Several extensions (tagfilter, autolink, table, strikethrough, tasklist, shortcodes)
          # are enabled by default in commonmarker 2.x but were strictly opt-in in 0.x. Explicitly
          # disable any extension the caller did not request so behavior matches the legacy contract.
          [:strikethrough, :tagfilter, :autolink, :table, :tasklist, :shortcodes].each do |ext|
            extension_options[ext] = false unless extension_options[ext]
          end

          # header_ids is enabled by default in commonmarker 2.x (it injects anchor tags inside
          # every heading). The legacy 0.x wrapper never enabled it implicitly, so disable it
          # unless the caller explicitly requested it.
          extension_options[:header_ids] = nil unless extension_options.key?(:header_ids)

          Commonmarker.to_html(
            content,
            options: {

View on GitHub (pinned to 76e2682193)

Solutions

  1. Keep only whitelisted extension symbols in commonmarker_exts (see the case list at markdown.rb:56-65).
  2. Move render/parse knobs to commonmarker_opts (e.g. hardbreaks belongs there as :HARDBREAKS).
  3. Convert strings to symbols before render: exts.map(&:to_sym) - then verify each survives the whitelist.
  4. Whitelist-filter user-provided extension lists so unsupported names are dropped instead of raising mid-request.

Example fix

# before
GitHub::Markup.render_s(:markdown, md, options: {
  commonmarker_exts: [:table, 'autolink', :hardbreaks]
})
# => ArgumentError: unknown commonmarker extension: "autolink" (then :hardbreaks)

# after
GitHub::Markup.render_s(:markdown, md, options: {
  commonmarker_opts: [:HARDBREAKS],
  commonmarker_exts: %i[table autolink tagfilter strikethrough]
})
Defensive patterns

Strategy: validation

Validate before calling

SUPPORTED_EXTS = %i[strikethrough tagfilter autolink table tasklist shortcodes footnotes
                    multiline_block_quotes math_dollars math_code wikilinks_title_after_pipe
                    wikilinks_title_before_pipe underline subscript spoiler greentext alerts
                    description_lists header_ids].freeze

def render_markdown(content, exts: [:table, :autolink, :strikethrough, :tagfilter])
  exts = Array(exts).map(&:to_sym)
  bad = exts - SUPPORTED_EXTS
  raise ArgumentError, "unsupported commonmarker_exts: #{bad.inspect}" unless bad.empty?
  GitHub::Markup.render_s(:markdown, content, options: {commonmarker_exts: exts})
end

Type guard

# Narrow extension input before it reaches the renderer:
def valid_commonmarker_exts?(exts)
  exts = Array(exts)
  exts.all? { |e| e.is_a?(Symbol) } && (exts - %i[strikethrough tagfilter autolink table
    tasklist shortcodes footnotes multiline_block_quotes math_dollars math_code
    wikilinks_title_after_pipe wikilinks_title_before_pipe underline subscript spoiler
    greentext alerts description_lists header_ids]).empty?
end

Try / catch

begin
  GitHub::Markup.render_s(:markdown, md, options: {commonmarker_exts: exts})
rescue ArgumentError => e
  raise unless e.message.start_with?('unknown commonmarker extension:')
  cleaned = exts.map(&:to_sym) & SUPPORTED_EXTS
  GitHub::Markup.render_s(:markdown, md, options: {commonmarker_exts: cleaned})
end

Prevention

When it happens

Trigger: Passing options: {commonmarker_exts: [:hardbreaks]} - hardbreaks is a render option, not an extension; passing legacy 0.x option symbols like :STRIKETHROUGH_DOUBLE_TILDE inside commonmarker_exts; sending strings ('table') instead of symbols; inventing extension names like :emoji or :mentions that commonmarker 2.x does not ship.

Common situations: Porting extension lists written for cmark-gfm or commonmarker 0.x; storing user rendering preferences as JSON so symbols arrive as strings; assuming a GitHub-flavored feature (e.g. emoji) is a commonmarker extension; typos such as :autolinks or :tasklists.

Related errors


AI-assisted analysis of github/markup@76e2682193 (2026-08-21). Data as JSON: /api/errors/09455cce9fd8854e. Report an issue: GitHub.