Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SyntaxError in an autoloading class referenced from a view causes "TypeError: Thread::Backtrace::Location object expected" #48326

Closed
zarqman opened this issue May 29, 2023 · 3 comments · Fixed by #48957

Comments

@zarqman
Copy link
Contributor

zarqman commented May 29, 2023

If a view renders code that autoloads a class that has a syntax error, DebugExceptions fails with a TypeError: Thread::Backtrace::Location object expected.

Backtrace
TypeError: Thread::Backtrace::Location object expected
 <internal:ast>:112:in `node_id_for_backtrace_location'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionview/lib/action_view/template.rb:172:in `spot'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb:252:in `spot'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb:302:in `extract_source'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb:206:in `block in source_extracts'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb:205:in `map'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb:205:in `source_extracts'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:125:in `create_template'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:77:in `render_for_browser_request'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:69:in `render_exception'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:44:in `rescue in call'
~/.rvm/gems/ruby-3.2.2@rails71/bundler/gems/rails-294be7c7de34/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'

Steps to reproduce

test.rb:

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails", github: "rails/rails", branch: "main"
  gem "rack", "~> 2.0"
end

require "action_controller/railtie"

# since DebugExceptions throws its own exception, display it here
module ActionDispatch
  class ShowExceptions
    def call(env)
      @app.call(env)
    rescue Exception => exception
      request = ActionDispatch::Request.new env
      backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
      wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
      puts "[SE] #{exception.inspect}"
      puts "[SE]   #{exception.backtrace.join("\n[SE]  ")}"
      if wrapper.show?(request)
        render_exception(request, wrapper)
      else
        raise exception
      end
    end
  end
end

class TestApp < Rails::Application
  config.root = __dir__
  config.hosts << "example.org"
  secrets.secret_key_base = "secret_key_base"
  config.consider_all_requests_local = true

  config.logger = Logger.new($stdout)
  Rails.logger  = config.logger

  routes.draw do
    get "/" => "test#index"
  end
end

class TestController < ActionController::Base
  include Rails.application.routes.url_helpers
  append_view_path '.'

  def index
    render '/view_test_1'
  end

  # simulate a syntax error in an external, auto-loading class
  def helper_1
    eval %(
      'abc' + pluralize 'def'
    )
  end
  helper_method :helper_1
end

require "minitest/autorun"
require "rack/test"

class BugTest < Minitest::Test
  include Rack::Test::Methods

  def test_returns_success
    get "/"
    # expecting a DebugExceptions response
    assert_match %r{<header role="banner">|<main role="main"}, last_response.body
  end

  private
    def app
      Rails.application
    end
end

view_test_1.html.erb (placed in same base dir as test.rb):

-view-
<%= helper_1 %>

Expected behavior

A standard DebugExceptions response should display the initial SyntaxError exception.

Actual behavior

DebugExceptions itself raises a TypeError with message Thread::Backtrace::Location object expected, in turn causing a generic 500 error to be returned.

System configuration

Rails version: main

Ruby version: 3.2.2

@cmaruz
Copy link
Contributor

cmaruz commented Aug 17, 2023

cc: @tenderlove

I tried to have a go at this issue and I have a proposed fix here: #48957

The error is raised inside MRI because the object we are passing to node_id_for_backtrace_location is not a backtrace location (https://github.com/ruby/ruby/blob/master/ast.c#L207). I tried to ensure we always pass a backtrace location without adding any is_a? check, by trying to keep with the original intent of the code.

Once the issue was fixed though, something interesting appeared - we were displaying the error page, but without any extracted source. I traced this back to the way syntax errors raised inside an eval call appear on the backtrace, so I have changed the way we build the backtrace in case of errors raised inside eval to be consistent with what the rest of the code is expecting.

It would be great to have any suggestions on how to approach it better!

@ILiuz
Copy link

ILiuz commented Oct 26, 2023

I am experiencing an issue similar to this one, but I think the conditions to reproduce it are a little bit different.

Steps to reproduce:

  • Generate a scaffold for e.g Post
  • Ensure one post is created
  • Edit /posts/_post.html.erb to have a syntax error

On Rails 7.1.1 and Ruby 3.2.2

It seems to me like as long as the syntax error is inside a partial used in a view, that is enough to get the same error from the modified ActionDispatch::ShowExceptions as was posted in the first post in this thread.
[SE] #<TypeError: Thread::Backtrace::Location object expected>
[SE] <internal:ast>:112:in node_id_for_backtrace_location' ...

I am hesitant to post a new issue even though the steps to produce seem different to me. Is this the same issue? Can anyone else verify that they are able to reproduce this by having a syntax error inside a partial rendered by a view?

@zarqman
Copy link
Contributor Author

zarqman commented Oct 26, 2023

@ILiuz Yes, I can confirm that a ruby syntax error in a partial causes the same error and backtrace on 7.1.1 here as well.

Since the backtrace is the same, I think it's another way of reproducing the same error and is seemingly the same issue.

Backtrace with line numbers from 7.1.1 (but otherwise the same)
#<TypeError: Thread::Backtrace::Location object expected>
  <internal:ast>:112:in `node_id_for_backtrace_location'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionview-7.1.1/lib/action_view/template.rb:219:in `spot'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/exception_wrapper.rb:252:in `spot'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/exception_wrapper.rb:302:in `extract_source'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/exception_wrapper.rb:206:in `block in source_extracts'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/exception_wrapper.rb:205:in `map'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/exception_wrapper.rb:205:in `source_extracts'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/debug_exceptions.rb:125:in `create_template'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/debug_exceptions.rb:77:in `render_for_browser_request'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/debug_exceptions.rb:69:in `render_exception'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/debug_exceptions.rb:44:in `rescue in call'
~/.rvm/gems/ruby-3.2.2@rails71/gems/actionpack-7.1.1/lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants