{"record":{"id":"dbf3f4b130ae5fca","repo":"Freika/dawarich","slug":"airtrail-responded-with-response-code","errorCode":null,"errorMessage":"AirTrail responded with #{response.code}","messagePattern":"AirTrail responded with #(.+?)","errorType":"exception","errorClass":"AirTrail::Client::Error","httpStatus":null,"severity":"error","filePath":"app/services/air_trail/client.rb","lineNumber":27,"sourceCode":"    def initialize(url, api_key, skip_ssl_verification: false)\n      @url = url.to_s.chomp('/')\n      @api_key = api_key\n      @skip_ssl_verification = skip_ssl_verification\n    end\n\n    def flights(scope: 'mine')\n      response = HTTParty.get(\n        \"#{@url}/api/flight/list?scope=#{scope}\",\n        http_options_with_ssl_flag(@skip_ssl_verification, {\n                                     headers: {\n                                       'Authorization' => \"Bearer #{@api_key}\",\n                                       'accept' => 'application/json'\n                                     },\n                                     timeout: 15\n                                   })\n      )\n\n      raise Error, \"AirTrail responded with #{response.code}\" unless response.success?\n\n      body = JSON.parse(response.body)\n      raise Error, 'AirTrail returned an unsuccessful response' unless body['success']\n\n      body['flights'] || []\n    rescue HTTParty::Error, Net::OpenTimeout, Net::ReadTimeout, JSON::ParserError => e\n      raise Error, e.message\n    end\n  end\nend\n","sourceCodeStart":9,"sourceCodeEnd":38,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/services/air_trail/client.rb#L9-L38","documentation":"AirTrail::Client#flights GETs {url}/api/flight/list?scope=... with a Bearer API key and a 15-second timeout; any non-2xx HTTP status raises AirTrail::Error with the numeric code embedded ('AirTrail responded with 401'). HTTParty errors, connection timeouts, and JSON parse failures are rescued separately and re-raised as Error with the underlying message - this particular message is exclusively the HTTP-status branch.","triggerScenarios":"401 when the configured AirTrail API key is wrong/revoked, 404 when the base URL points to an AirTrail instance whose version lacks /api/flight/list (older/self-hosted forks), 403 when the key's account cannot use scope=mine, 5xx when the AirTrail server is down or reverse-proxied to nothing.","commonSituations":"Typo in the AIRTRAIL_URL setting (missing / trailing path so the joined route 404s), rotated AirTrail API key not updated in Dawarich settings, self-hosted AirTrail upgraded/changed its API surface, TLS-terminating proxy returning 502 while the upstream restarts, scope parameter values the target instance does not support.","solutions":["Match the number: 401 -> fix the API key, 404 -> fix the URL/version, 403 -> key permissions/scope, 5xx -> AirTrail side is unhealthy","curl -H 'Authorization: Bearer <key>' {url}/api/flight/list?scope=mine to see the raw response outside Dawarich","Confirm the AirTrail instance version actually exposes /api/flight/list","Check the skip_ssl_verification setting matches the instance's certificate setup"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  AirTrail::Client.new(url:, api_key:, skip_ssl_verification:).flights(scope: 'mine')\nrescue AirTrail::Error => e\n  Rails.logger.warn(\"AirTrail sync failed: #{e.message}\")\n  # report to the user; do not retry blindly - 401/404 are deterministic failures\nend","preventionTips":["Validate the URL and key pair once with a cheap request when settings are saved, not at first sync","Distinguish status codes in handling: 401/403 are config errors, 5xx are transient","Keep the 15s timeout in mind - flaky upstreams surface as rescued timeout errors, not this message"],"tags":["integration","httparty","air-trail","flights","api"],"backgroundTag":"http-error-response","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}