instructure/canvas-lms · error
Error connecting to recaptcha #
Error message
Error connecting to recaptcha #{response} What it means
In UsersController#validate_recaptcha, when the HTTP response from Google's reCAPTCHA verify endpoint has a non-success status (the else branch), Canvas raises with the raw response. It indicates Canvas could not complete verification, not that the captcha was wrong.
Solutions
- Verify outbound HTTPS connectivity from the app server to https://www.google.com/recaptcha/api/siteverify
- Check proxy/firewall configuration for the Rails process
- Confirm reCAPTCHA site key and secret are valid in account settings
- Wrap the call and rescue to return a friendly 'could not verify captcha' response instead of a 500
Example fix
# before
raise "Error connecting to recaptcha #{response}"
# after
Rails.logger.error("reCAPTCHA verification request failed: #{response.code} #{response.body}")
return { errors: ["recaptcha-unreachable"] } Defensive patterns
Strategy: try-catch
Validate before calling
# ensure outbound reachability before user flows
code = Net::HTTP.get_response(URI('https://www.google.com/recaptcha/api/siteverify')).code rescue nil
ready = code == '200' || code == '405' # endpoint reachable (405 for GET is fine) Type guard
function recaptchaReachable(resp) {
return resp != null && typeof resp.code === 'number' && resp.code >= 200 && resp.code < 300;
} Try / catch
begin
errors = validate_recaptcha(params)
rescue RuntimeError => e
if e.message.start_with?('Error connecting to recaptcha')
flash[:error] = 'Could not verify captcha, please try again'
redirect_back
else
raise
end
end Prevention
- Configure and test outbound proxy/egress for the app servers
- Monitor Google reCAPTCHA service status
- Validate site key/secret in staging before production
- Add retry with backoff around siteverify calls
When it happens
Trigger: create_user with recaptcha enabled while Google's siteverify returns a transport-level failure: network outage, proxy/DNS failure, timeout, 5xx from Google, or missing/invalid recaptcha credentials causing an unexpected response status.
Common situations: Servers without outbound internet access; misconfigured proxy; invalid RECAPTCHA_SITE_KEY/SECRET; Google siteverify outage.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- Consul is unavailable because the circuit breaker has…
- could not retrieve configuration, the server response timed…
- Couldn't send LTI AGS grade progress metric
- Failed to fetch results for context #
- failed to load page view history due to service timeout
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/b02ade5622dbce0a.
Report an issue: GitHub.
Appendix: source
Thrown at app/controllers/users_controller.rb:3534
def validate_recaptcha(recaptcha_response)
# if there is no recaptcha key or recaptcha is disabled, don't do anything
return nil unless recaptcha_enabled?
# Authenticated API requests do not require a captcha
return nil unless @access_token.nil?
response = CanvasHttp.post("https://www.google.com/recaptcha/api/siteverify", form_data: {
secret: Rails.application.credentials.dig(:recaptcha_keys, :server_key),
response: recaptcha_response
})
if response && response.code == "200"
parsed = JSON.parse(response.body)
return { errors: parsed["error-codes"] } unless parsed["success"]
return { errors: ["invalid-hostname"] } unless parsed["hostname"] == request.host
nil
else
raise "Error connecting to recaptcha #{response}"
end
end
def locale_dates_for(course, current_course)
return { start_at_locale: nil, end_at_locale: nil } unless current_course&.locale.present?
I18n.with_locale(current_course.locale) do
{
start_at_locale: datetime_string(course.start_at, :verbose, nil, shorten_midnight: true),
end_at_locale: datetime_string(course.conclude_at, :verbose, nil, shorten_midnight: true)
}
end
end
def fetch_courses_with_grades(observed_user = nil)
target_user = observed_user || @current_user
# Use menu_courses to get filtered course list (handles favorites, invited enrollments, etc.)View on GitHub (pinned to 1c9f0bb801)