instructure/canvas-lms · error · ArgumentError
Cannot pass url and use block
Error message
Cannot pass url and use block
What it means
CanvasCrumb's add_crumb builds a before_action that records a navigation crumb. It supports either an explicit url argument or a block that computes the url at request time, but not both, since the block would be discarded or conflict with the static url. Passing both is a programming mistake, so it raises ArgumentError at class/body evaluation time.
Solutions
- Remove either the url argument or the block from the add_crumb call
- If the URL is dynamic, drop the url argument and keep the block (possibly returning a Symbol to call on the instance)
- If the URL is static, drop the block
Example fix
// before
add_crumb('Courses', courses_path) { |c| c.courses_path }
// after
add_crumb('Courses', courses_path)
# or dynamic:
add_crumb('Courses') { |c| c.courses_path } Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, 'choose url or block, not both' if url && block_given?
Type guard
def valid_crumb_args?(name: nil, url: nil, block_given:) !block_given? || url.nil? end
Prevention
- Pick one style per add_crumb call: static url OR block
- Code-review refactors that convert static crumbs to dynamic
- Lint for add_crumb calls with both a second positional arg and a block
When it happens
Trigger: Calling add_crumb('Name', '/some/path') (or with a full URL) together with a block: add_crumb('Name', url) { |c| c.polymorphic_path(...) }. Both the url positional/keyword argument and block_given? must be truthy simultaneously.
Common situations: Refactoring a crumb that used a static URL into a dynamic one and forgetting to remove the url argument; copy-pasting add_crumb calls and merging the two supported styles.
Related errors
- A new_id, '# ', referenced an existing # and the # with #…
- A new_integration_id, '#
- A student referenced a non-existent user #
- A user did not pass validation
- [A11Y Scan] Skipped resource #
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/45e3eab793c76332.
Report an issue: GitHub.
Appendix: source
Thrown at gems/canvas_crummy/lib/canvas_crummy/controller_methods.rb:40
module ClassMethods
# Add a crumb to the crumbs array.
#
# add_crumb("Home", "/")
# add_crumb("Business") { |instance| instance.business_path }
#
# Works like a before_filter so +:only+ and +except+ both work.
def add_crumb(name, *args)
options = args.extract_options!
url = args.first
# I wanted this next line to look more like:
# html_options = options.pluck { |k,v| %w{class id}.include? k.to_s }
# but couldn't figure out how
html_options = (klass = options.delete(:class)) ? { class: klass } : {}
if (id = options.delete(:id))
html_options[:id] = id
end
raise ArgumentError, "Need more arguments" unless name || block_given?
raise ArgumentError, "Cannot pass url and use block" if url && block_given?
before_action(options) do |instance|
url_value = url
url_value = yield instance if block_given?
url_value = instance.send url_value if url_value.is_a? Symbol
name_value = name
name_value = instance.instance_eval(&name_value) if name_value.is_a? Proc
name_value = instance.instance_variable_get(:"@#{name_value}") if name_value.is_a? Symbol
record = instance.instance_variable_get(:"@#{name_value}") unless url_value || block_given?
if record
name_value, url_value = record.to_s, instance.url_for(record)
end
# FIXME: url_value = instance.url_for(name_value) if name_value.respond_to?("to_param") && url_value.nil?
# FIXME: Add ||= for the name_value, url_value above
instance.add_crumb(name_value, url_value, html_options)
end
endView on GitHub (pinned to 1c9f0bb801)