{"record":{"id":"56778e9c9b2c92bb","repo":"instructure/canvas-lms","slug":"result-format-is-invalid-content-type","errorCode":null,"errorMessage":"Result format is invalid: #{content_type}","messagePattern":"Result format is invalid: #(.+?)","errorType":"exception","errorClass":"Common::InvalidResultError","httpStatus":null,"severity":"error","filePath":"app/services/page_views/fetch_batch_result_service.rb","lineNumber":43,"sourceCode":"        uri,\n        request_headers\n      ) do |response|\n        handle_generic_errors(response) unless response.code.to_i == 200\n        response.decode_content = false # Prevent automatic decompression\n        format = determine_result_format(response)\n        filename = determine_filename(response)\n        return Common::DownloadableResult.new(format:, filename:, content: response.body)\n      end\n    end\n\n    private\n\n    def determine_result_format(response)\n      raise InvalidResultError, \"Missing Content-Type header in response.\" unless response.header[\"Content-Type\"]\n\n      # strip any parameters (encoding for example) from the Content-Type\n      content_type = response.header[\"Content-Type\"].split(\";\").first.strip\n      raise Common::InvalidResultError, \"Result format is invalid: #{content_type}\" unless Common::CONTENT_TYPE_MAPPINGS[content_type]\n\n      Common::CONTENT_TYPE_MAPPINGS[content_type]\n    end\n\n    def determine_filename(response)\n      content_disposition = response.header[\"Content-Disposition\"]\n      if content_disposition && content_disposition =~ /filename=\"?([^\";]+)\"?/\n        Regexp.last_match(1)\n      else\n        raise Common::InvalidResultError, \"Unable to determine filename from Content-Disposition header\"\n      end\n    end\n  end\nend\n","sourceCodeStart":25,"sourceCodeEnd":58,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/page_views/fetch_batch_result_service.rb#L25-L58","documentation":"PageViews::FetchBatchResultService#determine_result_format raises Common::InvalidResultError when the response Content-Type (after stripping parameters like charset) is not a key in Common::CONTENT_TYPE_MAPPINGS. The service only knows how to handle the mapped formats (e.g. CSV/JSON), so anything else is rejected.","triggerScenarios":"Server responds with text/html (error page), application/json when only CSV is expected (or vice versa), text/plain, or a custom type not registered in CONTENT_TYPE_MAPPINGS.","commonSituations":"Auth failure returning an HTML login/error page; version drift where the API added a new format the client doesn't map; requesting a format the endpoint no longer supports; hitting an HTML 404/500 page.","solutions":["Check response.code for non-200 before parsing the body","Compare the returned Content-Type against the formats requested when enqueuing the query","Inspect response.body to identify the actual (likely HTML error) payload","Update/add the mapping in Common::CONTENT_TYPE_MAPPINGS if a new legitimate format was introduced"],"exampleFix":"// before\nraise \"bad format\" # vague; inspect first\n// after\nunless response.code == '200'\n  raise \"page views result fetch failed: #{response.code} #{response.body[0, 200]}\"\nend\nformat = service.call(response)","handlingStrategy":"try-catch","validationCode":"ctype = response.header['Content-Type'].to_s.split(';').first.to_s.strip\nraise \"unexpected content type: #{ctype}\" unless PageViews::Common::CONTENT_TYPE_MAPPINGS.key?(ctype)","typeGuard":null,"tryCatchPattern":"begin\n  result = service.call(response)\nrescue PageViews::Common::InvalidResultError => e\n  if e.message.start_with?('Result format is invalid')\n    Rails.logger.error(\"page views returned unsupported type: #{e.message} body=#{response.body[0, 200]}\")\n  end\n  raise\nend","preventionTips":["Check for HTML error pages (text/html) before parsing","Request only formats present in CONTENT_TYPE_MAPPINGS","Handle auth failures that return non-data content types","Keep CONTENT_TYPE_MAPPINGS in sync with API changes"],"tags":["http","content-type","response-parsing"],"backgroundTag":"unexpected-response-shape","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"}