{"record":{"id":"a32a6e28b4f3284a","repo":"flippercloud/flipper","slug":"failed-with-status-response-code","errorCode":null,"errorMessage":"Failed with status: #{response.code}","messagePattern":"Failed with status: #(.+?)","errorType":"exception","errorClass":"Flipper::Adapters::Http::Error","httpStatus":null,"severity":"error","filePath":"lib/flipper/adapters/http.rb","lineNumber":40,"sourceCode":"                             open_timeout: options[:open_timeout],\n                             write_timeout: options[:write_timeout],\n                             max_retries: options[:max_retries],\n                             debug_output: options[:debug_output])\n        @last_get_all_etag = nil\n        @last_get_all_result = nil\n        @last_get_all_response = nil\n        @get_all_mutex = Mutex.new\n      end\n\n      def get(feature)\n        response = @client.get(\"/features/#{path_escape(feature.key)}\")\n        if response.is_a?(Net::HTTPOK)\n          parsed_response = Typecast.from_json(response.body)\n          result_for_feature(feature, parsed_response.fetch('gates'))\n        elsif response.is_a?(Net::HTTPNotFound)\n          default_config\n        else\n          raise Error, response\n        end\n      end\n\n      def get_multi(features)\n        response = @client.get(\"/features?#{query_for_features(features)}\")\n        raise Error, response unless response.is_a?(Net::HTTPOK)\n\n        parsed_response = Typecast.from_json(response.body)\n        parsed_features = parsed_response.fetch('features')\n        gates_by_key = parsed_features.each_with_object({}) do |parsed_feature, hash|\n          hash[parsed_feature['key']] = parsed_feature['gates']\n          hash\n        end\n\n        result = {}\n        features.each do |feature|\n          result[feature.key] = result_for_feature(feature, gates_by_key[feature.key])\n        end","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/flippercloud/flipper/blob/1f86de3ec91521585b156445e156642938b19cb0/lib/flipper/adapters/http.rb#L22-L58","documentation":"The Http adapter talks to a Flipper API endpoint (Flipper Cloud at https://www.flippercloud.io/adapter, or a self-hosted flipper-api). get() issues GET /features/<key> and only accepts 200 (parse gates) or 404 (feature unknown, return defaults); every other status raises Flipper::Adapters::Http::Error. The message shows the status code plus any JSON `message`/`more_info` from the API body, and the full Net::HTTP response is attached as error.response for inspection.","triggerScenarios":"GET /features/<key> returning 401/403 (bad or missing flipper-cloud-token header / basic auth), 429 (Cloud rate limiting), 5xx (Cloud outage or self-hosted API error), or any non-200/404 from a URL that is not actually a Flipper API. Raised from feature checks (get, get_multi, get_all, features) when reads go over HTTP.","commonSituations":"Missing/rotated FLIPPER_CLOUD_TOKEN so every request 401s; URL pointed at the wrong place (the UI on port 9999 instead of the mounted /api path, or a typo'd host returning HTML error pages); expired/invalid Cloud environment token; a reverse proxy or WAF intercepting with 403/502; a Cloud incident returning 5xx.","solutions":["Check error.response.code and error.response.body (or set debug_output: $stderr on the adapter / FLIPPER_CLOUD_DEBUG_OUTPUT_STDOUT=1) to see the exact status and API message.","For 401/403: confirm the token header — Flipper Cloud needs FLIPPER_CLOUD_TOKEN set and the URL left at the default /adapter endpoint; rotate the token in Cloud settings if it is stale.","For wrong-endpoint responses: verify the url option actually reaches a Flipper API (self-hosted: https://app.example.com/flipper/api or wherever flipper-api is mounted), not the UI.","For 429/5xx transient failures: serve reads from a local cache (Cloud does this via local_adapter + DualWrite/Poll), or wrap the Http adapter with Flipper::Adapters::Failsafe/Failover plus Memoizable so checks degrade instead of raising.","Set explicit read/open/write timeouts on the adapter so hung endpoints fail fast rather than piling up."],"exampleFix":"# before\nadapter = Flipper::Adapters::Http.new(url: \"http://localhost:9999/\") # UI, not an API\nFlipper.new(adapter)[:search].enabled? # => Failed with status: 404/500\n\n# after\nhttp = Flipper::Adapters::Http.new(\n  url: ENV.fetch(\"FLIPPER_CLOUD_URL\", \"https://www.flippercloud.io/adapter\"),\n  headers: { \"flipper-cloud-token\" => ENV.fetch(\"FLIPPER_CLOUD_TOKEN\") },\n  read_timeout: 5, open_timeout: 2\n)\nflipper = Flipper.new(Flipper::Adapters::Failsafe.new(http, errors: [Flipper::Adapters::Http::Error]))","handlingStrategy":"fallback","validationCode":"require \"net/http\"\nuri = URI.join(flipper_url, \"/features/nonexistent_probe\")\nresponse = Net::HTTP.get_response(uri)\n# 200/401/403 tells you auth is the problem before wiring Flipper; a 404/HTML body tells you the URL is wrong\nputs response.code","typeGuard":null,"tryCatchPattern":"begin\n  Flipper[:search].enabled?\nrescue Flipper::Adapters::Http::Error => e\n  case e.response\n  when Net::HTTPUnauthorized, Net::HTTPForbidden then raise # config bug, fail loudly\n  else false # transient upstream failure: fail closed\n  end\nend","preventionTips":["Wrap the Http adapter in Flipper::Adapters::Failsafe or Failover plus Memoizable so reads degrade instead of raising.","Serve reads from a local adapter (Cloud's DualWrite + Poll design) and reserve HTTP for writes/sync.","Set read/open/write timeouts and keep tokens in env vars validated at boot.","Turn on debug_output when wiring the adapter to catch wrong-endpoint mistakes immediately."],"tags":["ruby","flipper","http-adapter","cloud","authentication","rest-api"],"backgroundTag":"http-non-2xx-response","analyzedSha":"1f86de3ec91521585b156445e156642938b19cb0","analyzedAt":"2026-08-23T04:36:09.896Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}