{"record":{"id":"b94d9a605c8ae246","repo":"instructure/canvas-lms","slug":"couldn-t-send-lti-ags-grade-progress-metric","errorCode":null,"errorMessage":"Couldn't send LTI AGS grade progress metric","messagePattern":"Couldn't send LTI AGS grade progress metric","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/controllers/lti/ims/scores_controller.rb","lineNumber":105,"sourceCode":"\n    MIME_TYPE = \"application/vnd.ims.lis.v1.score+json\"\n\n    def report_grade_progress_metric\n      dynamic_settings_tree = DynamicSettings.find(tree: :private)\n      if dynamic_settings_tree[\"frontend_data_collection_endpoint\"]\n        data_collection_endpoint = dynamic_settings_tree[\"frontend_data_collection_endpoint\"]\n        put_body = [{\n          id: SecureRandom.uuid,\n          type: \"ags_grade_progress\",\n          account_id: @domain_root_account.id.to_s,\n          account_name: @domain_root_account.name,\n          tool_domain: tool.domain,\n          grading_progress: params[:gradingProgress]\n        }]\n        CanvasHttp.put(data_collection_endpoint, {}, body: put_body.to_json, content_type: \"application/json\")\n      end\n    rescue\n      Rails.logger.warn(\"Couldn't send LTI AGS grade progress metric\")\n    end\n\n    # @API Create a Score\n    #\n    # Create a new Result from the score params. If this is for the first created line_item for a\n    # resourceLinkId, or it is a line item that is not attached to a resourceLinkId, then a submission\n    # record will be created for the associated assignment when gradingProgress is set to\n    # FullyGraded or PendingManual.\n    #\n    # The submission score will also be updated when a score object is sent with either of those\n    # two values for gradingProgress. If a score object is sent with either of FullyGraded or\n    # PendingManual as the value for gradingProgress and scoreGiven is missing, the assignment\n    # will not be graded. This also supposes the line_item meets the condition to create a submission.\n    #\n    # A submission comment with an unknown author will be created when the comment value is included.\n    # This also supposes the line_item meets the condition to create a submission.\n    #\n    # It is also possible to submit a file along with this score, which will attach the file to the","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/lti/ims/scores_controller.rb#L87-L123","documentation":"After creating/updating scores, ScoresController best-effort reports grading progress to the LTI tool's data collection endpoint via CanvasHttp.put. Any exception in that telemetry call (network failure, bad endpoint, timeout) is rescued and only logged as this warning — the main score operation is unaffected.","triggerScenarios":"CanvasHttp.put to data_collection_endpoint raises: DNS/connection failure to the tool domain, HTTP timeout, invalid/unreachable endpoint URL, or body serialization failure.","commonSituations":"Tool host down or firewalled; misconfigured AGS data_collection_endpoint on the LineItem/tool; CanvasHttp timeouts under load; egress blocked in the Canvas cluster.","solutions":["Check network egress from Canvas to the tool domain (curl the endpoint from the web container)","Verify the tool's AGS data_collection_endpoint configuration is a valid, reachable URL","Confirm CanvasHttp timeout and proxy settings","Ignore if intentional — grade sync succeeded; only the progress metric is lost"],"exampleFix":"// before\nCanvasHttp.put(data_collection_endpoint, {}, body: put_body.to_json, content_type: 'application/json')\n// after\nif data_collection_endpoint.present?\n  CanvasHttp.put(data_collection_endpoint, {}, body: put_body.to_json, content_type: 'application/json')\nend","handlingStrategy":"try-catch","validationCode":"return unless data_collection_endpoint.present? && data_collection_endpoint.start_with?('http')","typeGuard":"def valid_endpoint?(url)\n  uri = URI.parse(url) rescue nil\n  uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)\nend","tryCatchPattern":"begin\n  CanvasHttp.put(endpoint, {}, body: body.to_json, content_type: 'application/json')\nrescue StandardError => e\n  Rails.logger.warn(\"Couldn't send LTI AGS grade progress metric: #{e.class}: #{e.message}\")\nend","preventionTips":["Validate data_collection_endpoint at tool configuration time","Monitor CanvasHttp timeouts/egress from the Canvas cluster","Treat this metric as best-effort; keep the main score flow independent of it"],"tags":["lti","ags","network","http"],"backgroundTag":"http-request-failed","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}