instructure/canvas-lms · error · Lti::Errors::InvalidToolProxyError

Invalid Tool Proxy

Error message

Invalid Tool Proxy

What it means

Guard in Lti::IMS::ToolProxyController#re_reg: after authenticating the re-registration request (OAuth2 bearer token or shared-secret signature), tp_validator.valid? failed — the submitted Tool Proxy JSON doesn't validate against the LTI 2 schema/contract — so Lti::Errors::InvalidToolProxy is raised.

Solutions

  1. Fix the Tool Proxy payload so it passes the IMS ToolProxy validator (required capabilities, security contract, services)
  2. Compare against the originally registered ToolProxy JSON for missing fields
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at app/controllers/lti/ims/tool_proxy_controller.rb:113 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/2789f3cf1dc258a9. Report an issue: GitHub.

Appendix: source

Thrown at app/controllers/lti/ims/tool_proxy_controller.rb:113

      def re_reg
        tp = nil
        if oauth2_request?
          begin
            validate_access_token!
            tp = ToolProxy.find_by guid: access_token.sub
          rescue Lti::OAuth2::InvalidTokenError
            render_unauthorized and return
          end
        elsif request.authorization.present?
          tp = ToolProxy.find_by guid: oauth_consumer_key
          render_unauthorized and return unless oauth_authenticated_request?(tp.shared_secret)
        else
          render_unauthorized and return
        end

        unless tp_validator.valid?
          raise Lti::Errors::InvalidToolProxyError.new "Invalid Tool Proxy", tp_validator.errors.as_json
        end

        json = {
          "@context" => "http://purl.imsglobal.org/ctx/lti/v2/ToolProxyId",
          "@type" => "ToolProxy",
          "@id" => tp.raw_data["@id"],
          "tool_proxy_guid" => tp.guid
        }

        tps = ToolProxyService.new
        tps.create_secret(::IMS::LTI::Models::ToolProxy.from_json(payload))

        tp.update_payload = {
          acknowledgement_url: request.headers["VND-IMS-CONFIRM-URL"],
          payload: JSON.parse(payload)
        }

        if (tc_half_secret = tps.tc_half_secret)

View on GitHub (pinned to 1c9f0bb801)