{"record":{"id":"c98bcc19a6201b9c","repo":"we-promise/sure","slug":"unauthorized-c98bcc","errorCode":"unauthorized","errorMessage":"Invalid API key","messagePattern":"Invalid API key","errorType":"exception","errorClass":"LunchflowError","httpStatus":401,"severity":"error","filePath":"app/models/provider/lunchflow.rb","lineNumber":129,"sourceCode":"  private\n\n    def auth_headers\n      {\n        \"x-api-key\" => api_key,\n        \"Content-Type\" => \"application/json\",\n        \"Accept\" => \"application/json\"\n      }\n    end\n\n    def handle_response(response)\n      case response.code\n      when 200\n        JSON.parse(response.body, symbolize_names: true)\n      when 400\n        Rails.logger.error \"Lunch Flow API: Bad request - #{response.body}\"\n        raise LunchflowError.new(\"Bad request to Lunch Flow API: #{response.body}\", :bad_request)\n      when 401\n        raise LunchflowError.new(\"Invalid API key\", :unauthorized)\n      when 403\n        raise LunchflowError.new(\"Access forbidden - check your API key permissions\", :access_forbidden)\n      when 404\n        raise LunchflowError.new(\"Resource not found\", :not_found)\n      when 429\n        raise LunchflowError.new(\"Rate limit exceeded. Please try again later.\", :rate_limited)\n      else\n        Rails.logger.error \"Lunch Flow API: Unexpected response - Code: #{response.code}, Body: #{response.body}\"\n        raise LunchflowError.new(\"Failed to fetch data: #{response.code} #{response.message} - #{response.body}\", :fetch_failed)\n      end\n    end\n\n    class LunchflowError < StandardError\n      attr_reader :error_type\n\n      def initialize(message, error_type = :unknown)\n        super(message)\n        @error_type = error_type","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/lunchflow.rb#L111-L147","documentation":"HTTP 401 branch of Provider::Lunchflow#handle_response (app/models/provider/lunchflow.rb:128-129). Thrown as LunchflowError(:unauthorized) with message 'Invalid API key' whenever Lunchflow answers any endpoint with 401, meaning the x-api-key header sent by auth_headers (line 115) is missing, malformed, revoked, or simply wrong. Note: in get_accounts/get_account_transactions/get_account_balance/get_account_holdings this error is subsequently re-wrapped by the method-level catch-all into :request_failed (message becomes 'Exception during GET request: Invalid API key').","triggerScenarios":"Any client call where the stored API key fails authentication: key with leading/trailing whitespace or a newline from copy-paste, a key revoked or rotated in the Lunchflow dashboard, a key from a different Lunchflow workspace/environment, or a Mercury 'secret-token:' accidentally saved in the Lunchflow settings.","commonSituations":"User re-issued keys in the provider dashboard but did not update them in the app; secrets copied with quotes or padding; staging key used against production base_url (or vice versa); CI running without the provider credentials seeded.","solutions":["Re-copy the API key from the Lunchflow dashboard into the provider settings, stripping whitespace/newlines.","Verify the key out-of-band: curl -H 'x-api-key: <key>' https://lunchflow.app/api/v1/accounts — 200 confirms the key, 401 confirms the key is bad.","Confirm the key belongs to the same account/workspace the base_url targets.","Handle :unauthorized in the caller by marking the connection as needing re-auth rather than retrying."],"exampleFix":"// before\nclient = Provider::Lunchflow.new(api_key)\nclient.get_accounts\n\n// after\nraise ArgumentError, 'Lunchflow API key is missing' if api_key.blank?\nclient = Provider::Lunchflow.new(api_key.strip)\nclient.get_accounts","handlingStrategy":"validation","validationCode":"raise ArgumentError, 'API key missing' if api_key.to_s.strip.empty?\nclient = Provider::Lunchflow.new(api_key.to_s.strip)","typeGuard":"def unauthorized?(e)\n  e.is_a?(Provider::Lunchflow::LunchflowError) &&\n    (e.error_type == :unauthorized || e.message.include?('Invalid API key'))\nend","tryCatchPattern":"begin\n  client.get_accounts\nrescue Provider::Lunchflow::LunchflowError => e\n  raise unless unauthorized?(e)\n  connection.update!(status: 'reauth_required')\n  notify_user_to_reenter_lunchflow_key(connection)\nend","preventionTips":["Strip whitespace/newlines when storing provider keys.","Verify new keys with one cheap get_accounts call at save time.","Never auto-retry 401s — prompt re-authentication instead."],"tags":["lunchflow","http-401","api-key","authentication","ruby"],"backgroundTag":"invalid-api-key","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}