instructure/canvas-lms · error · Lti::IMS::AdvantageErrors::InvalidLaunchError
Assignment not configured for launches with specified tool
Error message
Assignment not configured for launches with specified tool
What it means
AssignmentResourceLinkRequest#assignment_resource_link raises InvalidLaunchError when the assignment targeted by an LTI resource link launch cannot be launched with the current tool. Two checks fail it: the assignment must be an external-tool assignment, and the tool resolved from the assignment must match the launching tool. Otherwise the launch is rejected before any resource link is resolved.
Solutions
- Set the assignment's submission_types to 'external_tool' and attach an external tool tag pointing at the target tool.
- Launch from the tool (client_id) actually configured on the assignment's external_tool tag.
- Rebuild the assignment's tool tag after a course copy if it was dropped or re-linked to a different tool.
- Handle InvalidLaunchError in the launch controller and surface a friendly error to the LTI platform.
Example fix
// before (assignment not a tool assignment) assignment.update(submission_types: 'online_text_entry') // after assignment.update(submission_types: 'external_tool') assignment.build_external_tool_tag(content: tool, url: tool.config_for(domain).first[:url]).save!
Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError unless assignment.external_tool? raise ArgumentError unless Lti::ToolFinder.from_assignment(assignment) == tool
Type guard
def launchable_with?(assignment, tool) assignment.external_tool? && Lti::ToolFinder.from_assignment(assignment) == tool end
Try / catch
begin
assignment_resource_link
rescue Lti::IMS::AdvantageErrors::InvalidLaunchError => e
Rails.logger.warn("launch rejected: #{e.message}")
render json: { errors: e.to_s }, status: :unprocessable_entity
end Prevention
- Always create tool assignments with submission_types: 'external_tool' and a saved external tool tag.
- After course copies/imports, verify external tool tags still point at the intended tool.
- Launch only with the client_id/tool registered on the assignment.
- Catch InvalidLaunchError at the launch controller boundary and return a friendly API error.
When it happens
Trigger: POSTing an LTI launch for an assignment whose submission_types are not 'external_tool', or launching with a different LTI::Tool (client_id/developer_key) than the one configured on the assignment's external tool tag.
Common situations: Course copy/import loses or re-points the external tool tag so the assignment resolves to a different tool; devs reuse a tool's launch URL on a regular (online) assignment; multiple tools installed and the launch comes through the wrong client_id.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Assignment not configured for external tool launches
- Access token expired
- Access token invalid - signature likely incorrect
- assignment_invalid
- assignment_tool_mismatch
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/6e803d5c5234ae02.
Report an issue: GitHub.
Appendix: source
Thrown at lib/lti/messages/resource_link_request.rb:107
def unexpanded_custom_parameters
# Add in link-specific custom params (e.g. created by deep linking)
super.merge(resource_link&.custom || {})
end
def resource_link
assignment_resource_link || @opts[:resource_link]
end
def assignment_resource_link
return if @assignment.nil?
unless defined?(@assignment_resource_link)
launch_error = Lti::IMS::AdvantageErrors::InvalidLaunchError
unless @assignment.external_tool?
raise launch_error.new(nil, api_message: "Assignment not configured for external tool launches")
end
unless Lti::ToolFinder.from_assignment(@assignment) == @tool
raise launch_error.new(nil, api_message: "Assignment not configured for launches with specified tool")
end
@assignment_resource_link = line_item_for_assignment&.resource_link
end
@assignment_resource_link
end
def line_item_for_assignment
@line_item_for_assignment ||= @assignment&.line_items&.find(&:assignment_line_item?)
end
# follows the spec at https://www.imsglobal.org/spec/lti-ags/v2p0/#assignment-and-grade-service-claim
# and only adds the 'lineitem' property "when the LTI message is launching a resource associated
# to one and only one line item"
def add_line_item_url_to_ags_claim!
return if line_item_for_assignment.blank?
View on GitHub (pinned to 1c9f0bb801)