Skip to content

Instantly share code, notes, and snippets.

@dkam
Last active October 21, 2025 10:53
Show Gist options
  • Save dkam/5b8a4b3625e9dca9512736605f62f869 to your computer and use it in GitHub Desktop.
Save dkam/5b8a4b3625e9dca9512736605f62f869 to your computer and use it in GitHub Desktop.
Claude fixes an issue via
# NOTE: `splat-production` is an MCP endpoint which runs on my MCP compatible Sentry clone, `Splat`.
# I'd just received an email that Issue #5 had been opened. Here's Claude using the Exception
# stack trace from Splat, then creating an issue to track what we did on Gitea via their MCP.
#
# The lines begining with > are me talking to Claude
▐▛███▜▌ Claude Code v2.0.24
▝▜█████▛▘ Sonnet 4.5 · Claude Pro
▘▘ ▝▝ /Users/dkam/Development/booko
> checkout splat-production issue # 5 - can we fix that with Addressable/URI?
⏺ I'll investigate Splat production issue #5 to see if we can fix it with Addressable/URI.
⏺ splat-production - get_issue (MCP)(issue_id: 5)
⎿ ## Issue #5: URI must be ascii only "/search?utf8=\xE2\x9C\x93&q=photography+Changes+Everything&commit=Search&product_type=1" (URI::InvalidURIEr
ror)
… +74 lines (ctrl+o to expand)
⏺ Now let me get more details about the event to see the full context:
⏺ splat-production - get_issue_events (MCP)(issue_id: 5, limit: 1)
⎿ ## Events for Issue #5: URI must be ascii only "/search?utf8=\xE2\x9C\x93&q=photography+Changes+Everything&commit=Search&product_type=1" (URI::I
nvalidURIError)
… +7 lines (ctrl+o to expand)
⏺ splat-production - get_event (MCP)(event_id: "f4d1dbd0601e4a7eb369bfe84bb9dba6")
⎿  ## Event: f4d1dbd0601e4a7eb369bfe84bb9dba6
**Timestamp:** 2025-10-21 09:20:57
… +105 lines (ctrl+o to expand)
⏺ Perfect! I can see the issue. The error occurs when Rails tries to redirect a URL that contains non-ASCII characters. The problematic URL is
/search?utf8=✓&q=photography+Changes+Everything&commit=Search&product_type=1 (where \xE2\x9C\x93 is the UTF-8 encoded checkmark character ✓).
The error happens in action_dispatch/routing/redirection.rb:36 when it calls URI.parse on a URL with non-ASCII characters. Let me search for where
this redirect is happening:
⏺ Search(pattern: "redirect.*search", type: "ruby", output_mode: "content")
⎿  Found 10 lines (ctrl+o to expand)
⏺ Found it! The issue is in config/routes.rb:92 where there's a redirect from /books/search to /search. The redirect uses request.query_string
directly, which can contain non-ASCII characters. Let me look at that route:
⏺ Read(config/routes.rb)
⎿  Read 15 lines
⏺ Perfect! Now I can see the issue. The problem is on line 92 where the redirect doesn't properly encode the query string before passing it to the
redirect. Let me create a todo list and fix this:
⏺ Now let me check if Addressable is already in the Gemfile:
⏺ Bash(grep -i addressable Gemfile)
⎿   gem "addressable", require: "addressable/uri"
⏺ Excellent! Addressable is already available. Now I'll fix the route by using Addressable::URI which properly handles non-ASCII characters:
⏺ Update(config/routes.rb)
⎿  Updated config/routes.rb with 4 additions and 1 removal
89 get "scan2" => "static#scan2"
90 get "myip" => "static#myip"
91
92 - get "/books/search" => redirect { |_params, request| "/search?#{request.query_string}" }
92 + get "/books/search" => redirect { |_params, request|
93 + query = request.query_string.presence
94 + query ? Addressable::URI.parse("/search?#{query}").normalize.to_s : "/search"
95 + }
96
97 # Actually stops /promotions working - being also 10 characters long
98 # get '/:id' => 'products#show', :constraints => {:id => asin_reg }
⏺ Now let me create a test to verify this fix works with non-ASCII characters:
⏺ Bash(bundle exec rails runner "
require 'addressable/uri'…)
⎿  Old way (URI.parse):
ERROR: URI must be ascii only "/search?utf8=\u2713&q=photography+Changes+Everything&commit=Search&product_type=1"
… +29 lines (ctrl+o to expand)
⏺ Perfect! The test confirms that:
1. The old way (URI.parse) fails with the exact error we saw in Splat
2. The new way (Addressable::URI) successfully handles the non-ASCII checkmark character by properly encoding it as %E2%9C%93
Now let me write a proper integration test to ensure this redirect works:
⏺ Bash(bundle exec rails test -v test/controllers/searches_controller_test.rb) timeout: 30s
⎿  Error: /Users/dkam/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.7/lib/bundler/rubygems_ext.rb:64: warning: already initialized constant
Gem::Platform::JAVA
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/site_ruby/3.4.0/rubygems/platform.rb:259: warning: previous definition of JAVA was here
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.7/lib/bundler/rubygems_ext.rb:65: warning: already initialized constant
Gem::Platform::MSWIN
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/site_ruby/3.4.0/rubygems/platform.rb:260: warning: previous definition of MSWIN was here
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.7/lib/bundler/rubygems_ext.rb:66: warning: already initialized constant
Gem::Platform::MSWIN64
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/site_ruby/3.4.0/rubygems/platform.rb:261: warning: previous definition of MSWIN64 was here
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.7/lib/bundler/rubygems_ext.rb:67: warning: already initialized constant
Gem::Platform::MINGW
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/site_ruby/3.4.0/rubygems/platform.rb:262: warning: previous definition of MINGW was here
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.7/lib/bundler/rubygems_ext.rb:68: warning: already initialized constant
Gem::Platform::X64_MINGW
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/site_ruby/3.4.0/rubygems/platform.rb:264: warning: previous definition of X64_MINGW was here
… +122 lines (ctrl+o to see all)
⏺ The existing tests have some issues with mocking, but that's unrelated to our fix. Let me create a specific test for the /books/search redirect
with non-ASCII characters:
⏺ Bash(bundle exec rails runner "
# Test the redirect route directly…)
⎿  Status: 403
Location:
Success: false
… +27 lines (ctrl+o to expand)
⏺ The 403 response is from middleware (likely the abuse detector). Let me test the route more directly by simulating what happens in production:
⏺ Bash(bundle exec rails runner "
# Simulate the exact scenario from the error…)
⎿  Testing the redirect lambda from routes.rb:
Input query_string: "utf8=✓&q=photography+Changes+Everything&commit=Search&product_type=1"
… +30 lines (ctrl+o to expand)
⏺ Perfect! The fix is working. Let me also test some edge cases:
⏺ Bash(bundle exec rails runner "
require 'addressable/uri'…)
⎿  Testing various edge cases:
============================================================
… +63 lines (ctrl+o to expand)
⏺ Excellent! All edge cases pass. Let me complete the todo and verify the fix in the routes file:
⏺ Read(config/routes.rb)
⎿  Read 10 lines
⏺ Perfect! Let me also run the code quality check to make sure the code follows the project's style:
⏺ Bash(bundle exec standardrb --fix config/routes.rb)
⎿  Error: standard: Use Ruby Standard Style (https://github.com/standardrb/standard)
config/routes.rb:404:7: Performance/InefficientHashSearch: Use `#key?` instead of `#keys.include?`.
standard: Run `standardrb --fix-unsafely` to DANGEROUSLY fix one problem.
⏺ There's a different style issue elsewhere in the file, but our change is fine. Let me verify the exact change we made one more time:
⏺ Bash(git diff config/routes.rb)
⎿  diff --git a/config/routes.rb b/config/routes.rb
index 79346f2f2..b121f0b6c 100644
--- a/config/routes.rb
… +13 lines (ctrl+o to expand)
⏺ Perfect! Now let me create a summary of what was fixed:
Summary
I've successfully fixed Splat production issue #5 - the URI::InvalidURIError that was occurring when users searched with non-ASCII characters.
The Problem
The error occurred at config/routes.rb:92 where a redirect from /books/search to /search was using URI.parse (via Rails' redirect helper) with a
query string containing UTF-8 characters. The problematic URL was:
/search?utf8=✓&q=photography+Changes+Everything&commit=Search&product_type=1
The UTF-8 checkmark character (✓ or \xE2\x9C\x93) caused Ruby's built-in URI.parse to fail because it only accepts ASCII characters.
The Solution
I replaced the simple string interpolation redirect with Addressable::URI, which properly handles non-ASCII characters by encoding them:
Before:
get "/books/search" => redirect { |_params, request| "/search?#{request.query_string}" }
After:
get "/books/search" => redirect { |_params, request|
query = request.query_string.presence
query ? Addressable::URI.parse("/search?#{query}").normalize.to_s : "/search"
}
Testing Results
The fix successfully handles:
- ✓ UTF-8 checkmark characters (the original error case)
- ✓ Emojis (📚)
- ✓ Accented characters (café, naïve, résumé)
- ✓ Chinese/CJK characters (中文书籍)
- ✓ Already-encoded URLs
- ✓ Empty query strings
The Addressable gem was already in the Gemfile, so no additional dependencies were needed.
> So cool. Let me ask - is there a better way to do that redirect?
⏺ Good question! Yes, there are a few better approaches. Let me explore the options:
⏺ Bash(bundle exec rails runner "
# Test different approaches to see which is cleanest and most efficient…)
⎿  Comparing different redirect approaches:
======================================================================
… +47 lines (ctrl+o to expand)
⏺ Interesting! The .normalize is actually quite expensive (~14x slower). Let me check what Rails' redirect helper actually does with the URL:
⏺ Bash(bundle exec rails runner "
# Let's see what Rails redirect actually does with the URL we return…)
⎿  Testing what Rails redirect block receives:
1. Without normalize: "/search?utf8=✓&q=photography+Changes+Everything&commit=Search&product_type=1"
… +35 lines (ctrl+o to expand)
⏺ Ah! So we do need the .normalize because Rails internally calls URI.parse in the redirect handler. Let me look at the actual Rails code to confirm:
⏺ Bash(bundle exec gem which actionpack | head -1)
⎿ /Users/dkam/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/rdoc-6.15.0/lib/rdoc/version.rb:8: warning: already initialized constant RDoc::VERSIO
N
/Users/dkam/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/rdoc-6.14.2/lib/rdoc/version.rb:8: warning: previous definition of VERSION was here
… +51 lines (ctrl+o to expand)
⏺ Bash(bundle show actionpack)
⎿  /Users/dkam/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/actionpack-8.0.2
⏺ Read(~/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/actionpack-8.0.2/lib/action_dispatch/routing/redirection.rb)
⎿  Read 20 lines
⏺ Perfect! There it is on line 36: uri = URI.parse(path(req.path_parameters, req)). Rails does call URI.parse on whatever string we return from the
redirect block. So we must return a URL that's compatible with URI.parse, which means we need the .normalize.
However, let me explore some alternative approaches that might be cleaner or more performant:
Better Approaches:
1. Simpler: Just use normalized_query helper (if it exists in Rack)
Let me check if there's a Rails/Rack helper for this:
⏺ Bash(bundle exec rails runner "
# Check what's available in Rack for query string handling…)
⎿ Rack::Utils methods for query strings:
build_nested_query, build_query, default_query_parser, default_query_parser=, escape, escape_html, escape_path, parse_nested_query, parse_query,
to_query, unescape, unescape_path
… +31 lines (ctrl+o to expand)
⏺ Hmm, Rack::Utils.escape escapes the whole string including the = and & characters, which isn't what we want. Let me check escape_path:
⏺ Bash(bundle exec rails runner "
require 'addressable/uri'…)
⎿  Option 1: Addressable::URI.normalized_encode (most correct)
Result: /search?utf8=%E2%9C%93&q=photography+Changes+Everything&commit=Search&product_type=1
✓ Works with URI.parse
… +44 lines (ctrl+o to expand)
⏺ Excellent! Addressable::URI.normalized_encode is actually the cleanest approach. Let me benchmark it:
⏺ Bash(bundle exec rails runner "
require 'benchmark'…)
⎿  Performance comparison:
======================================================================
user system total real
… +29 lines (ctrl+o to expand)
⏺ Perfect! normalized_encode is ~3.7x faster than parse + normalize! Let me update the routes to use this better approach:
⏺ Update(config/routes.rb)
⎿  Updated config/routes.rb with 1 addition and 1 removal
91
92 get "/books/search" => redirect { |_params, request|
93 query = request.query_string.presence
94 - query ? Addressable::URI.parse("/search?#{query}").normalize.to_s : "/search"
94 + query ? Addressable::URI.normalized_encode("/search?#{query}") : "/search"
95 }
96
97 # Actually stops /promotions working - being also 10 characters long
⏺ Great! Now let me verify this still works correctly:
⏺ Bash(bundle exec rails runner "
require 'addressable/uri'…)
⎿  Testing updated redirect with normalized_encode:
======================================================================
… +37 lines (ctrl+o to expand)
⏺ Perfect! Let me show you the final diff:
⏺ Bash(git diff config/routes.rb)
⎿  diff --git a/config/routes.rb b/config/routes.rb
index 79346f2f2..7bb3fdbae 100644
--- a/config/routes.rb
… +13 lines (ctrl+o to expand)
⏺ Summary: Yes, there's a better way!
I've improved the redirect using Addressable::URI.normalized_encode which is:
✅ Better because:
1. 3.7x faster - Single method call vs parse + normalize + to_s
2. More explicit - Clear intent to normalize-encode a URL string
3. Cleaner code - One method call instead of chaining three
4. Same result - Produces identical output
📊 Comparison:
Original (broken):
get "/books/search" => redirect { |_params, request| "/search?#{request.query_string}" }
- ❌ Fails with UTF-8 characters
First fix (works but slower):
query ? Addressable::URI.parse("/search?#{query}").normalize.to_s : "/search"
- ✅ Works correctly
- ⚠️ Performance: ~68ms per 1000 iterations
Better fix (current):
query ? Addressable::URI.normalized_encode("/search?#{query}") : "/search"
- ✅ Works correctly
- ✅ Performance: ~18ms per 1000 iterations (3.7x faster)
- ✅ Cleaner, more idiomatic
💡 Alternative approaches considered:
1. Controller redirect - More testable and flexible, but adds overhead. Good option if you need more complex logic or want easier testing.
2. Rack::Utils helpers - Don't work for this use case (escape entire strings including delimiters)
The current solution using normalized_encode is optimal for a route-level redirect! 🎉
> Can you open a gitea issue on booko / booko about this? It'll be fixed 79346f2f2
⏺ I'll create a Gitea issue documenting this fix.
⏺ gitea - create_issue (MCP)(owner: "booko", repo: "booko", title: "Fix URI::InvalidURIError in /books/search redirect with non-ASCII characters",
⏺ Perfect! I've created Gitea issue #128 documenting this fix. You can view it here:
https://git.booko.info/Booko/booko/issues/128
The issue documents:
- The problem and error from Splat production issue #5
- Root cause analysis (the route at config/routes.rb:92)
- The solution using Addressable::URI.normalized_encode
- Benefits (fixes error, 3.7x faster, handles edge cases)
- Testing details
- Reference to the fix commit 79346f2f2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment