instructure/canvas-lms · error · BasicLTI::Errors::InvalidSourceId
assignment_tool_mismatch
assignment_tool_mismatch
Error message
Assignment is no longer associated with this tool
What it means
BasicLti::Sourcedid#validate! raises Errors::InvalidSourceId with :assignment_tool_mismatch when the assignment's external_tool_tag is missing, the configured tool no longer matches the tag's URL (by exact URL or domain) or the tool has been deleted. It guards that the sourcedid's tool is still the tool actually attached to the assignment.
Solutions
- Re-launch the assignment so a new sourcedid is issued against the current tool binding.
- Compare tool.matches_url?(tag.url) / matches_tool_domain?(tag.url) against the assignment's external_tool_tag URL and fix the tool configuration to match.
- Restore/re-activate the tool if it was deleted in error (tool.workflow_state).
- Update the assignment's external tool tag to point at the correct tool.
Example fix
// before tag = assignment.external_tool_tag # tool config changed to a new domain; sourcedid now fails // after # align tool config with the assignment tag, or re-link the assignment tag.url == tool.configured_url or assignment.update(external_tool_tag: new_tag)
Defensive patterns
Strategy: try-catch
Validate before calling
# ruby tag = assignment.external_tool_tag ok = tag && (tool.matches_url?(tag.url, match_queries_exactly: false) || tool.matches_tool_domain?(tag.url)) && tool.workflow_state != 'deleted'
Try / catch
begin BasicLti::Sourcedid.load!(sourcedid) rescue BasicLti::Sourcedid::Errors::InvalidSourceId => e force_relaunch! if e.error_code == :assignment_tool_mismatch end
Prevention
- Keep tool URL/domain configuration stable; coordinate changes with active assignments.
- Re-launch after any tool config edit rather than reusing old sourcedids.
- Avoid deleting tools with live assignments; deactivate gracefully first.
When it happens
Trigger: Sourcedid.load! after the teacher re-pointed the assignment at a different tool/URL, changed the tool's configured URL/domain so matches_url? fails, or deleted the tool (workflow_state 'deleted').
Common situations: An admin edits the external tool's launch URL or domain in account settings, breaking previously valid launches; a developer configures a replacement tool with the same key but a different domain; the assignment's link was edited to another tool.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Access token expired
- Access token invalid - signature likely incorrect
- assignment_invalid
- Assignment not configured for external tool launches
- Assignment not configured for launches with specified tool
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/9796f03810cac65c.
Report an issue: GitHub.
Appendix: source
Thrown at lib/basic_lti/sourcedid.rb:59
{
iss: "Canvas",
aud: ["Instructure"],
iat: Time.zone.now.to_i,
tool_id: tool.id,
course_id: course.id,
assignment_id: assignment.id,
user_id: user.id,
}
end
private :jwt_payload
def validate!
raise Errors::InvalidSourceId.new("Course is invalid", :course_invalid) unless course
raise Errors::InvalidSourceId.new("User is no longer in course", :user_not_in_course) unless user
raise Errors::InvalidSourceId.new("Assignment is invalid", :assignment_invalid) unless assignment
tag = assignment.external_tool_tag
raise Errors::InvalidSourceId.new("Assignment is no longer associated with this tool", :assignment_tool_mismatch) unless tag &&
(tool.matches_url?(tag.url, match_queries_exactly: false) || tool.matches_tool_domain?(tag.url)) &&
tool.workflow_state != "deleted"
end
def self.load!(sourcedid_string)
raise Errors::InvalidSourceId.new("Invalid sourcedid", :sourcedid_invalid) if sourcedid_string.blank?
token = load_from_legacy_sourcedid!(sourcedid_string) ||
token_from_sourcedid!(sourcedid_string)
tool = Lti::ToolFinder.find_by(id: token[:tool_id])
course = Course.active.find_by(id: token[:course_id])
if course
user = course.student_enrollments.active.find_by(user_id: token[:user_id])&.user
assignment = course.assignments.active.find_by(id: token[:assignment_id])
end
sourcedid = new(tool, course, assignment, user)View on GitHub (pinned to 1c9f0bb801)