Last active
October 21, 2025 10:53
-
-
Save dkam/5b8a4b3625e9dca9512736605f62f869 to your computer and use it in GitHub Desktop.
Claude fixes an issue via
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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