instructure/canvas-lms · error · Lti::Errors::InvalidToolProxyError
#
Error message
#{message} What it means
Lti::ToolProxyValidator#validate! accumulates error symbols (invalid capabilities, capabilities not allowed, invalid security contract/profiles, restricted security profiles) and, if any exist, raises Lti::Errors::InvalidToolProxyError with a joined human-readable message (e.g. 'Invalid Capabilities and Invalid SecurityContract') plus the errors hash. This guards tool proxy registration payloads against TC policy violations.
Solutions
- Read the raised message and errors hash; fix each named section (capabilities, security contract, security profiles) in the tool proxy JSON.
- Only request capabilities Canvas allows for the placement/tool type; remove unsupported ones.
- Declare valid security profiles (e.g. HalfSharedSecret/SharedSecret with correct services and actions).
- Re-submit the registration with the corrected payload.
Example fix
// before
{ "enabled_capability": ["vnd.instructure.some.unsupported.cap"], ... }
// after
{ "enabled_capability": ["basic-lti-launch-request", "Security.splitSecret"], ... } Defensive patterns
Strategy: validation
Validate before calling
validator = Lti::ToolProxyValidator.new(tp_json, context)
unless validator.errors.empty?
raise "invalid tool proxy: #{validator.errors.keys.join(', ')}"
end Type guard
def valid_security_profiles?(tp_json)
required = %w[SecurityProfile_1.0]
(tp_json['security_profile'] && required.any? { |r| tp_json['security_profile']['security_profile_name']&.include?(r) })
end Try / catch
begin
tp_service.process_tool_proxy_form
rescue Lti::Errors::InvalidToolProxyError => e
render json: { errors: e.errors }, status: :bad_request
end Prevention
- Validate tool proxy JSON against the LTI 2.0 spec before registration.
- Only enable capabilities Canvas supports for the placement.
- Keep vendor registration payloads under version control and test in beta first.
When it happens
Trigger: Registering/validating a tool proxy (POST of the tool proxy JSON) whose capabilities, security profiles, or security contract fail any of the validator checks — wrong enabled_capabilities, unsupported security profile, or a security contract not matching Canvas requirements.
Common situations: Tool vendors shipping proxy JSON referencing capabilities Canvas does not allow; missing/wrong security profiles (e.g. not declaring SharedSecret or split secret correctly); registration payloads generated against a different LTI spec version.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Developer key mismatch
- e.message
- EventType # is invalid
- Invalid url in xml. Ampersands must be escaped.
- Invalid XML Configuration
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/1aedb893ab65fded.
Report an issue: GitHub.
Appendix: source
Thrown at lib/lti/tool_proxy_validator.rb:53
hash.merge!(invalid_service_errors)
hash.merge!(invalid_security_contract_errors)
hash.merge!(invalid_security_profile_errors)
hash.merge!(restricted_security_profile_errors)
end
end
def validate!
if errors.present?
messages = []
messages << "Invalid Capabilities" if errors[:invalid_capabilities].present?
messages << "Invalid Services" if errors[:invalid_services].present?
messages << "Invalid SecurityContract" if errors[:invalid_security_contract].present?
messages << "Invalid SecurityProfiles" if errors[:invalid_security_profiles].present?
messages << "Restricted SecurityProfiles" if errors[:restricted_security_profiles].present?
last_message = messages.pop if messages.size > 1
message = messages.join(", ")
message + " and #{last_message}" if last_message
raise Lti::Errors::InvalidToolProxyError.new message, errors
end
end
private
def invalid_capability_errors
messages = {}
if validator.errors[:invalid_message_handlers]
messages[:invalid_capabilities] = validator.errors[:invalid_message_handlers][:resource_handlers].map do |rh|
rh[:messages].map do |message|
message[:invalid_capabilities] || message[:invalid_parameters].pluck(:variable)
end
end.flatten
end
messages
end
def invalid_service_errorsView on GitHub (pinned to 1c9f0bb801)