{"record":{"id":"a227369fdae54d0a","repo":"instructure/canvas-lms","slug":"tii-returned-resp-status-code-content-length-body-length","errorCode":null,"errorMessage":"TII returned #{resp.status} code, content length=#{body&.length}, message #{error_msg}, body #{body&.truncate(100).inspect}","messagePattern":"TII returned #(.+?) code, content length=#(.+?), message #(.+?), body #(.+?)","errorType":"exception","errorClass":"InvalidResponse","httpStatus":null,"severity":"error","filePath":"gems/turnitin_api/lib/turnitin_api/outcomes_response_transformer.rb","lineNumber":62,"sourceCode":"    def initialize(key, secret, lti_params, outcomes_response_json)\n      @key = key\n      @secret = secret\n      @lti_params = lti_params || {}\n      @outcomes_response_json = outcomes_response_json\n    end\n\n    def response\n      @response ||= make_call(outcomes_response_json[\"outcomes_tool_placement_url\"]).tap do |resp|\n        next resp if (200..299).cover?(resp.status)\n\n        error_msg = KNOWN_ERROR_MESSAGES.find { |_name, text| resp.body&.include?(text) }&.first\n        error_msg ||= :unknown\n\n        stats_tags = { status: resp.status, message: error_msg }\n        InstStatsd::Statsd.distributed_increment(\"lti.tii.outcomes_response_bad\", tags: stats_tags)\n\n        body = resp.env[:raw_body] || resp.body\n        raise InvalidResponse,\n              \"TII returned #{resp.status} code, content length=#{body&.length}, \" \\\n              \"message #{error_msg}, body #{body&.truncate(100).inspect}\"\n      end\n    end\n\n    # download original\n    def original_submission\n      yield make_call(response.body[\"outcome_originalfile\"][\"launch_url\"])\n    end\n\n    # store link to report\n    def originality_report_url\n      response.body[\"outcome_originalityreport\"][\"launch_url\"]\n    end\n\n    def originality_data\n      response.body[\"outcome_originalityreport\"].slice(\"breakdown\", \"numeric\")\n    end","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/gems/turnitin_api/lib/turnitin_api/outcomes_response_transformer.rb#L44-L80","documentation":"The Turnitin outcomes response transformer raises InvalidResponse when the upstream TII API reply is not a usable success: error_msg or a bad HTTP status indicates failure. It embeds status code, body length, error message, and a truncated body for diagnostics, after emitting a statsd metric.","triggerScenarios":"TII API returning non-success HTTP status; response missing expected fields (error_msg defaults to :unknown); empty or malformed body from the Turnitin service.","commonSituations":"Turnitin service outage or degradation; invalid/expired TII API credentials causing auth failures; network proxies returning HTML error pages; TII integration misconfiguration in account settings.","solutions":["Check TII service status and retry once the upstream recovers","Verify Turnitin account credentials and API endpoint configuration","Inspect the logged body snippet and statsd tags (lti.tii.outcomes_response_bad) to identify the upstream error","Add retry/backoff handling around TII outcomes calls for transient 5xx responses"],"exampleFix":"// before\nresp = transformer.response // raises InvalidResponse on TII failure\n// after\nbegin\n  resp = transformer.response\nrescue TurnitinApi::InvalidResponse => e\n  logger.warn(\"TII failed: #{e.message}\")\n  retry with backoff\nend","handlingStrategy":"retry","validationCode":"if (!resp.success?) {\n  logger.warn(`TII bad status ${resp.status}, deferring`);\n  return schedule_retry();\n}","typeGuard":null,"tryCatchPattern":"begin\n  transformer.response\nrescue TurnitinApi::InvalidResponse => e\n  Statsd.increment('tii.failure')\n  retry with backoff or surface to user\nend","preventionTips":["Monitor the lti.tii.outcomes_response_bad metric","Validate TII credentials and endpoint config regularly","Implement exponential backoff for transient upstream failures","Alert on sustained TII error rates"],"tags":["turnitin","lti","upstream","http"],"backgroundTag":"upstream-api-error","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"}