{"record":{"id":"b02ade5622dbce0a","repo":"instructure/canvas-lms","slug":"error-connecting-to-recaptcha-response","errorCode":null,"errorMessage":"Error connecting to recaptcha #{response}","messagePattern":"Error connecting to recaptcha #(.+?)","errorType":"exception","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/controllers/users_controller.rb","lineNumber":3534,"sourceCode":"  def validate_recaptcha(recaptcha_response)\n    # if there is no recaptcha key or recaptcha is disabled, don't do anything\n    return nil unless recaptcha_enabled?\n    # Authenticated API requests do not require a captcha\n    return nil unless @access_token.nil?\n\n    response = CanvasHttp.post(\"https://www.google.com/recaptcha/api/siteverify\", form_data: {\n                                 secret: Rails.application.credentials.dig(:recaptcha_keys, :server_key),\n                                 response: recaptcha_response\n                               })\n\n    if response && response.code == \"200\"\n      parsed = JSON.parse(response.body)\n      return { errors: parsed[\"error-codes\"] } unless parsed[\"success\"]\n      return { errors: [\"invalid-hostname\"] } unless parsed[\"hostname\"] == request.host\n\n      nil\n    else\n      raise \"Error connecting to recaptcha #{response}\"\n    end\n  end\n\n  def locale_dates_for(course, current_course)\n    return { start_at_locale: nil, end_at_locale: nil } unless current_course&.locale.present?\n\n    I18n.with_locale(current_course.locale) do\n      {\n        start_at_locale: datetime_string(course.start_at, :verbose, nil, shorten_midnight: true),\n        end_at_locale: datetime_string(course.conclude_at, :verbose, nil, shorten_midnight: true)\n      }\n    end\n  end\n\n  def fetch_courses_with_grades(observed_user = nil)\n    target_user = observed_user || @current_user\n\n    # Use menu_courses to get filtered course list (handles favorites, invited enrollments, etc.)","sourceCodeStart":3516,"sourceCodeEnd":3552,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/users_controller.rb#L3516-L3552","documentation":"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.","triggerScenarios":"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.","commonSituations":"Servers without outbound internet access; misconfigured proxy; invalid RECAPTCHA_SITE_KEY/SECRET; Google siteverify outage.","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"],"exampleFix":"# before\nraise \"Error connecting to recaptcha #{response}\"\n# after\nRails.logger.error(\"reCAPTCHA verification request failed: #{response.code} #{response.body}\")\nreturn { errors: [\"recaptcha-unreachable\"] }","handlingStrategy":"try-catch","validationCode":"# ensure outbound reachability before user flows\ncode = Net::HTTP.get_response(URI('https://www.google.com/recaptcha/api/siteverify')).code rescue nil\nready = code == '200' || code == '405' # endpoint reachable (405 for GET is fine)","typeGuard":"function recaptchaReachable(resp) {\n  return resp != null && typeof resp.code === 'number' && resp.code >= 200 && resp.code < 300;\n}","tryCatchPattern":"begin\n  errors = validate_recaptcha(params)\nrescue RuntimeError => e\n  if e.message.start_with?('Error connecting to recaptcha')\n    flash[:error] = 'Could not verify captcha, please try again'\n    redirect_back\n  else\n    raise\n  end\nend","preventionTips":["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"],"tags":["recaptcha","network","third-party-api"],"backgroundTag":"http-request-failed","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"}