instructure/canvas-lms · error
Empty value
Error message
Empty value
What it means
ContextExternalTool.context_id_for HMACs the asset_string of a context asset using the shard's encryption key, and raises "Empty value" if the asset's asset_string is blank — a blank asset cannot produce a stable context id.
Solutions
- Ensure the context record is persisted (has an id) before computing context ids
- Pass an actual context asset (Account/Course/etc.) rather than an arbitrary object
- Verify the object responds to asset_string with a non-blank value
Example fix
// before context_id_for(unsaved_course, shard) // after raise 'course must be saved' if unsaved_course.new_record? context_id_for(unsaved_course, shard)
Defensive patterns
Strategy: validation
Validate before calling
raise 'asset_string required' if asset.nil? || asset.asset_string.to_s.strip.empty?
Type guard
->(a) { a.respond_to?(:asset_string) && a.asset_string.present? } Try / catch
begin
tool.build_context_id(asset, shard)
rescue RuntimeError => e
raise e unless e.message == 'Empty value'
Rails.logger.error("blank asset_string for #{asset.class}")
end Prevention
- Persist records before computing context ids
- Only pass AR context assets (Course, Account, etc.) into context_id_for
- Assert asset_string presence in launch-setting builders
When it happens
Trigger: Calling context_id_for with an asset whose asset_string is empty (e.g. unsaved record with nil id, or an object that does not implement asset_string meaningfully), or a nil/non-asset argument stringified to empty.
Common situations: Building tool launch settings for an unsaved context; passing a plain string/hash instead of an AR asset; models missing the asset_string method so to_s yields "".
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
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/a262e59d6ddc39d8.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/context_external_tool.rb:347
on_by_default: tool.on_by_default?(on_by_default_ids),
description: if tool.description
Sanitize.clean(markdown.render(tool.description), CanvasSanitize::SANITIZE)
else
""
end
}
end
end
def on_by_default_ids
Setting.get("rce_always_on_developer_key_ids", "").split(",").reject(&:empty?).map(&:to_i)
end
private
def context_id_for(asset, shard)
str = asset.asset_string.to_s
raise "Empty value" if str.blank?
Canvas::Security.hmac_sha1(str, shard.settings[:encryption_key])
end
def global_navigation_permissions_to_check(root_account)
# look at the list of tools that are configured for the account and see if any are asking for permissions checks
Rails.cache.fetch_with_batched_keys("external_tools/global_navigation/permissions_to_check", batch_object: root_account, batched_keys: :global_navigation) do
tools = all_global_navigation_tools(root_account)
tools.filter_map { |tool| tool.extension_setting(:global_navigation, "required_permissions")&.split(",")&.map(&:to_sym) }.flatten.uniq
end
end
def all_global_navigation_tools(root_account)
RequestCache.cache("global_navigation_tools", root_account) do # prevent re-querying
Lti::ContextToolFinder.new(root_account, type: :global_navigation).all_tools_scope_union.to_unsorted_array
end
end
View on GitHub (pinned to 1c9f0bb801)