{"record":{"id":"3fe303524e2362d9","repo":"Freika/dawarich","slug":"airtrail-returned-an-unsuccessful-response","errorCode":null,"errorMessage":"AirTrail returned an unsuccessful response","messagePattern":"AirTrail returned an unsuccessful response","errorType":"exception","errorClass":"AirTrail::Client::Error","httpStatus":null,"severity":"error","filePath":"app/services/air_trail/client.rb","lineNumber":30,"sourceCode":"      @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":12,"sourceCodeEnd":38,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/services/air_trail/client.rb#L12-L38","documentation":"After a 2xx response, the AirTrail client parses the body and expects the envelope {success: true, flights: [...]}. If body['success'] is falsy, it raises 'AirTrail returned an unsuccessful response' - the HTTP layer was fine but the application layer inside AirTrail refused the operation. This is a 200-with-error-envelope contract, so the HTTP code alone cannot diagnose it.","triggerScenarios":"AirTrail returning 200 with {success: false} - e.g. the key authenticates but the account has no flight permission for the requested scope, an internal AirTrail error surfaced as success:false, or an API version whose envelope semantics changed (success field absent also counts as falsy).","commonSituations":"AirTrail version drift changing the response envelope (dropping or renaming 'success'), scope values (mine vs all) the instance rejects at app level, partially-deployed AirTrail instances answering with error JSON behind a 200, rate-limit style responses wrapped in 200.","solutions":["Inspect the raw AirTrail response body (log response.body temporarily) - the reason for success:false is usually inside it","Verify the AirTrail version's /api/flight/list contract still uses {success, flights}","Confirm the API key's account can read the requested scope","Try scope variations against the instance directly with curl"],"exampleFix":"# before\nbody = JSON.parse(response.body)\nraise Error, 'AirTrail returned an unsuccessful response' unless body['success']\n\n# after (surface the reason without leaking secrets)\nbody = JSON.parse(response.body)\nraise Error, \"AirTrail returned an unsuccessful response: #{body['error'] || body['message']}\" unless body['success']","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  client.flights\nrescue AirTrail::Error => e\n  Sentry.capture_message('airtrail_envelope_failure', level: :warning, extra: { message: e.message })\n  raise\nend","preventionTips":["Pin or verify the AirTrail version whose envelope includes success/flights before enabling the integration","Log response.body on envelope failures (it contains the app-level reason) - but scrub any auth material","Add a contract test against a recorded AirTrail response so envelope drift is caught in CI"],"tags":["integration","httparty","air-trail","flights","api-envelope"],"backgroundTag":"api-error-envelope","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}