{"record":{"id":"785cb29e4441d0a3","repo":"instructure/canvas-lms","slug":"missing-content-type-header-in-response","errorCode":null,"errorMessage":"Missing Content-Type header in response.","messagePattern":"Missing Content-Type header in response\\.","errorType":"exception","errorClass":"InvalidResultError","httpStatus":null,"severity":"error","filePath":"app/services/page_views/fetch_batch_result_service.rb","lineNumber":39,"sourceCode":"  class FetchBatchResultService < PageViews::ServiceBase\n    def call(query_id)\n      uri = @configuration.uri.merge(\"/api/v5/pageviews/batch-query/#{query_id}/results\")\n      get_with_clean_redirect(\n        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","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/page_views/fetch_batch_result_service.rb#L21-L57","documentation":"PageViews::FetchBatchResultService#determine_result_format raises InvalidResultError when the HTTP response has no Content-Type header. The service maps Content-Type to a result format via Common::CONTENT_TYPE_MAPPINGS and cannot decide how to process/deliver the body without it.","triggerScenarios":"The batch result endpoint returned a response with a missing/blank Content-Type header — typically an error page, redirect, or misconfigured proxy/gateway response passed to call.","commonSituations":"Reverse proxy stripping headers; the remote service returning an empty error response; hitting the wrong URL (e.g. HTML 404 page without proper content type); network middleware interference in dev.","solutions":["Log and inspect response.header and response.code before processing to see what the server actually returned","Verify the result URL is the one returned by the async query status endpoint","Check proxy/load-balancer config for header stripping (Content-Type)","Retry the fetch; transient gateway errors often lack proper headers"],"exampleFix":"// before\nformat = service.call(response) # raises if header missing\n// after\nunless response.header['Content-Type']\n  raise \"unexpected response (code=#{response.code}): #{response.body[0, 200]}\"\nend\nformat = service.call(response)","handlingStrategy":"try-catch","validationCode":"raise 'missing Content-Type from page views API' if response.header['Content-Type'].blank?","typeGuard":null,"tryCatchPattern":"begin\n  result = PageViews::FetchBatchResultService.new(config).call(response)\nrescue PageViews::InvalidResultError => e\n  Rails.logger.error(\"bad page views response: #{e.message} (code=#{response.code})\")\n  raise\nend","preventionTips":["Check response.code is 200 before processing the body","Log response headers on unexpected responses for debugging","Watch for proxies stripping Content-Type","Retry transient gateway failures"],"tags":["http","response-parsing","content-type"],"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"}