instructure/canvas-lms · error · Common::InvalidResultError
Unable to determine filename from Content-Disposition header
Error message
Unable to determine filename from Content-Disposition header
What it means
PageViews::FetchResultService#determine_filename raises Common::InvalidResultError when the Content-Disposition header is missing or contains no filename= attribute matching /filename=?([^";]+)"?/. The service derives the download filename (and gzip detection via the .gz suffix) from this header, so without it a DownloadableResult cannot be built.
Solutions
- Inspect the raw response headers for the failing request to confirm Content-Disposition is absent vs malformed
- Bypass or reconfigure any proxy/CDN between Canvas and the page-views service so Content-Disposition passes through untouched
- Check the upstream page-views service version/release notes for changes to the results endpoint's headers
- As a robustness fix, add a fallback filename (e.g. "pageviews-#{query_id}.csv") when the header is missing
Example fix
// before
filename = determine_filename(response).delete_suffix(".gz")
// after
def determine_filename(response)
content_disposition = response.header["Content-Disposition"]
if content_disposition && content_disposition =~ /filename="?([^";]+)"?/
Regexp.last_match(1)
else
"pageviews-result-#{@query_id}.csv" # fallback instead of raise
end
end Defensive patterns
Strategy: try-catch
Validate before calling
def has_filename?(response) cd = response.header["Content-Disposition"].to_s cd.match?(/filename="?([^";]+)"?/) end
Type guard
def filename_from(response) response.header["Content-Disposition"]&.match(/filename="?([^";]+)"?/)&.captures&.first end
Try / catch
begin
result = service.call(query_id)
rescue PageViews::Common::InvalidResultError => e
if e.message.include?("Content-Disposition")
Rails.logger.warn("missing filename header for query #{query_id}")
end
raise
end Prevention
- Configure proxies/CDNs to pass Content-Disposition through unchanged
- Verify the upstream results endpoint still sets filename after service upgrades
- Add a fallback filename derived from query_id if you control the client code
- Curl the endpoint and inspect headers when filename errors appear
When it happens
Trigger: Calling FetchResultService#call(query_id) (directly or via response_compressed?) when the results response has no Content-Disposition header, or one without a filename parameter (e.g. 'inline', 'attachment' with no filename, or a header stripped by an intermediate proxy/CDN).
Common situations: A reverse proxy or CDN (e.g. CloudFront, nginx) strips or rewrites Content-Disposition; the upstream service was updated and no longer sets filename on the results endpoint; the request actually hit an error/interstitial page rather than the file download.
Related errors
- invalid request
- Result format is invalid: #
- Unable to determine filename from Content-Disposition header
- unauthorized request
- Content Export for Outcomes Service failed
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/96b31c070cc89d28.
Report an issue: GitHub.
Appendix: source
Thrown at app/services/page_views/fetch_result_service.rb:54
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)
(response.header["Content-Encoding"] && response.header["Content-Encoding"] == "gzip") ||
determine_filename(response).end_with?(".gz")
end
end
end
View on GitHub (pinned to 1c9f0bb801)