Freika/dawarich · error · AirTrail::Client::Error
AirTrail returned an unsuccessful response
Error message
AirTrail returned an unsuccessful response
What it means
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.
Source
Thrown at app/services/air_trail/client.rb:30
@skip_ssl_verification = skip_ssl_verification
end
def flights(scope: 'mine')
response = HTTParty.get(
"#{@url}/api/flight/list?scope=#{scope}",
http_options_with_ssl_flag(@skip_ssl_verification, {
headers: {
'Authorization' => "Bearer #{@api_key}",
'accept' => 'application/json'
},
timeout: 15
})
)
raise Error, "AirTrail responded with #{response.code}" unless response.success?
body = JSON.parse(response.body)
raise Error, 'AirTrail returned an unsuccessful response' unless body['success']
body['flights'] || []
rescue HTTParty::Error, Net::OpenTimeout, Net::ReadTimeout, JSON::ParserError => e
raise Error, e.message
end
end
end
View on GitHub (pinned to 97fad417c5)
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
Example fix
# before
body = JSON.parse(response.body)
raise Error, 'AirTrail returned an unsuccessful response' unless body['success']
# after (surface the reason without leaking secrets)
body = JSON.parse(response.body)
raise Error, "AirTrail returned an unsuccessful response: #{body['error'] || body['message']}" unless body['success'] Defensive patterns
Strategy: try-catch
Try / catch
begin
client.flights
rescue AirTrail::Error => e
Sentry.capture_message('airtrail_envelope_failure', level: :warning, extra: { message: e.message })
raise
end Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
AI-assisted analysis of Freika/dawarich@97fad417c5 (2026-08-21).
Data as JSON: /api/errors/3fe303524e2362d9.
Report an issue: GitHub.