instructure/canvas-lms · error · InvalidResultError

Missing Content-Type header in response.

Error message

Missing Content-Type header in response.

What it means

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.

Solutions

  1. Log and inspect response.header and response.code before processing to see what the server actually returned
  2. Verify the result URL is the one returned by the async query status endpoint
  3. Check proxy/load-balancer config for header stripping (Content-Type)
  4. Retry the fetch; transient gateway errors often lack proper headers

Example fix

// before
format = service.call(response) # raises if header missing
// after
unless response.header['Content-Type']
  raise "unexpected response (code=#{response.code}): #{response.body[0, 200]}"
end
format = service.call(response)
Defensive patterns

Strategy: try-catch

Validate before calling

raise 'missing Content-Type from page views API' if response.header['Content-Type'].blank?

Try / catch

begin
  result = PageViews::FetchBatchResultService.new(config).call(response)
rescue PageViews::InvalidResultError => e
  Rails.logger.error("bad page views response: #{e.message} (code=#{response.code})")
  raise
end

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at app/services/page_views/fetch_batch_result_service.rb:39

  class FetchBatchResultService < PageViews::ServiceBase
    def call(query_id)
      uri = @configuration.uri.merge("/api/v5/pageviews/batch-query/#{query_id}/results")
      get_with_clean_redirect(
        uri,
        request_headers
      ) do |response|
        handle_generic_errors(response) unless response.code.to_i == 200
        response.decode_content = false # Prevent automatic decompression
        format = determine_result_format(response)
        filename = determine_filename(response)
        return Common::DownloadableResult.new(format:, filename:, content: response.body)
      end
    end

    private

    def determine_result_format(response)
      raise InvalidResultError, "Missing Content-Type header in response." unless response.header["Content-Type"]

      # strip any parameters (encoding for example) from the Content-Type
      content_type = response.header["Content-Type"].split(";").first.strip
      raise Common::InvalidResultError, "Result format is invalid: #{content_type}" unless Common::CONTENT_TYPE_MAPPINGS[content_type]

      Common::CONTENT_TYPE_MAPPINGS[content_type]
    end

    def determine_filename(response)
      content_disposition = response.header["Content-Disposition"]
      if content_disposition && content_disposition =~ /filename="?([^";]+)"?/
        Regexp.last_match(1)
      else
        raise Common::InvalidResultError, "Unable to determine filename from Content-Disposition header"
      end
    end
  end
end

View on GitHub (pinned to 1c9f0bb801)