instructure/canvas-lms · error · InvalidResultError
Missing Content-Type header in response.
Error message
Missing Content-Type header in response.
What it means
PageViews::FetchResultService#determine_result_format raises InvalidResultError when the single-result HTTP response has no Content-Type header. Like the batch variant, format selection depends on Common::CONTENT_TYPE_MAPPINGS keyed by Content-Type; without the header the body cannot be interpreted.
Solutions
- Inspect response.code and response.header before calling the service; handle non-200 explicitly
- Retry with backoff — transient gateway failures often produce header-less responses
- Check proxy/CDN configuration for header stripping
- Verify the async query completed (poll the status endpoint) before fetching results
Example fix
// before
result = PageViews::FetchResultService.new(config).call(response)
// after
if response.header['Content-Type'].blank?
raise "page views result unavailable (code=#{response.code})"
end
result = PageViews::FetchResultService.new(config).call(response) Defensive patterns
Strategy: try-catch
Validate before calling
raise 'missing Content-Type' if response.header['Content-Type'].blank? raise 'non-200 from page views API' unless response.code == '200'
Try / catch
begin
result = PageViews::FetchResultService.new(config).call(response)
rescue PageViews::InvalidResultError => e
Rails.logger.error("page views fetch failed: #{e.message} (code=#{response.code})")
raise
end Prevention
- Verify the async job finished before fetching results
- Check response.code and headers before processing
- Use retries with backoff for transient gateway errors
- Ensure proxies preserve Content-Type headers
When it happens
Trigger: The result endpoint returned a response without Content-Type — typically an empty gateway error, a dropped-header proxy response, or a raw connection failure object passed to call.
Common situations: Upstream outage returning malformed responses; misconfigured reverse proxy stripping Content-Type; DNS/proxy issues in dev returning non-standard responses; polling the result URL before the job finished and receiving a stub response.
Related errors
- Missing Content-Type header in response.
- Result format is invalid: #
- Content-Type must be 'application/xml'
- Result format is invalid: #
- Unable to determine filename from Content-Disposition header
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/7959826a2dd7c640.
Report an issue: GitHub.
Appendix: source
Thrown at app/services/page_views/fetch_result_service.rb:40
def call(query_id)
uri = @configuration.uri.merge("/api/v5/pageviews/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).delete_suffix(".gz")
compressed = response_compressed?(response)
return Common::DownloadableResult.new(format:, filename:, content: response.body, compressed?: compressed)
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
def response_compressed?(response)View on GitHub (pinned to 1c9f0bb801)