Skip to content

[BUG] contenttypes keys with Lua pattern special characters (e.g. +) fail to match response Content-Type headersΒ #837

@Chris-Robertson

Description

@Chris-Robertson

Description

Content types containing characters that are special in Lua patterns (such as +, -, .) fail to match response Content-Type headers when used as keys in the contenttypes config option.

Steps to Reproduce

Add the following to your kulala config:

contenttypes = {
    ["application/hal+json"] = "application/json",
}

Make a request to an endpoint that responds with Content-Type: application/hal+json. The response body will be treated as plain text instead of JSON.

The response buffer :set filetype? will return text.kulala_ui, when it should be json.kulala_ui.

Impact

The response body is not formatted.

This may also affect the built-in default mapping ["application/graphql-response+json"], which contains both + and -, both special characters in Lua patterns, meaning that mapping may also not work as intended. Though I have not tested this.

Root Cause

In

local config_key = vim.iter(CONFIG.get().contenttypes):find(function(k, _)
return content_type:match(k)
end)
, the contenttypes key is used directly as a Lua pattern.

Because k is passed to string.match as a pattern rather than a plain string, any key containing Lua pattern special characters (+, -, ., *, ?, [, ], ^, $, %, (, )) will not behave as expected.

For example, the key "application/hal+json" is interpreted as the pattern application/hal+json, where l+ means "one or more l characters".

Workaround

Escape special characters manually in your config keys using %

contenttypes = {
    ["application/hal%+json"] = "application/json",
}

Suggested Fix

Escape keys with vim.pesc() before using them as patterns.

local config_key = vim.iter(CONFIG.get().contenttypes):find(function(k, _)
  return content_type:match(vim.pesc(k))
end)

I would be happy to raise a PR for this, but I wanted to check I'm not barking up the wrong tree first.

Request

Any request that responds with Content-Type: application/hal+json.

Here is an example from https://httpbin.org/

GET https://httpbin.org/response-headers?Content-Type=application/hal+json

Error

No error happens, but the response body is not formatted.

Health


System:
β„Ή {OS} Darwin 25.3.0
β„Ή {Neovim} version 0.12.0
β„Ή {kulala.nvim} version 5.3.4

Tools:
βœ” {cURL} found: /usr/bin/curl (version: 8.7.1)
✘ {gRPCurl} not found
✘ {websocat} not found
βœ” {openssl} found: /opt/homebrew/bin/openssl (version: 3.6.1)
βœ” {NPM} found: /Users/chris.robertson/.asdf/shims/npm (version: unknown)

Formatters:
βœ” {application/json} formatter: jq .
βœ” {application/javascript} formatter: prettier --stdin-filepath file.js
⚠ {application/lua} formatter not found
βœ” {application/graphql} formatter: prettier --stdin-filepath file.graphql
βœ” {text/html} formatter: prettier --stdin-filepath file.html
βœ” {application/graphql-response+json} formatter: jq .
βœ” {application/xml} formatter: xmllint --format -
βœ” {application/hal+json} formatter: jq .

User Config

{
  additional_curl_options = { "--insecure" },
  contenttypes = {
    ["application/hal+json"] = "application/json"
  },
  debug = 4,
  default_env = "tunnel",
  global_keymaps = true,
  urlencode = "always"
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions