{"record":{"id":"ea21507d7de60908","repo":"we-promise/sure","slug":"not-found-ea2150","errorCode":"not_found","errorMessage":"Resource not found","messagePattern":"Resource not found","errorType":"exception","errorClass":"Provider::Mercury::MercuryError","httpStatus":404,"severity":"error","filePath":"app/models/provider/mercury.rb","lineNumber":124,"sourceCode":"        \"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 \"Mercury API: Bad request - #{response.body}\"\n        raise MercuryError.new(\"Bad request to Mercury API: #{response.body}\", :bad_request)\n      when 401\n        # Parse the error response for more specific messages\n        error_message = parse_error_message(response.body)\n        raise MercuryError.new(error_message, :unauthorized)\n      when 403\n        raise MercuryError.new(\"Access forbidden - check your API token permissions\", :access_forbidden)\n      when 404\n        raise MercuryError.new(\"Resource not found\", :not_found)\n      when 429\n        raise MercuryError.new(\"Rate limit exceeded. Please try again later.\", :rate_limited)\n      else\n        Rails.logger.error \"Mercury API: Unexpected response - Code: #{response.code}, Body: #{response.body}\"\n        raise MercuryError.new(\"Failed to fetch data: #{response.code} #{response.message} - #{response.body}\", :fetch_failed)\n      end\n    end\n\n    def parse_error_message(body)\n      parsed = JSON.parse(body, symbolize_names: true)\n      errors = parsed[:errors] || {}\n\n      case errors[:errorCode]\n      when \"ipNotWhitelisted\"\n        ip = errors[:ip] || \"unknown\"\n        \"IP address not whitelisted (#{ip}). Add your IP to the API token's whitelist in Mercury dashboard.\"\n      when \"noTokenInDBButMaybeMalformed\"\n        \"Invalid token format. Make sure to include the 'secret-token:' prefix.\"","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/mercury.rb#L106-L142","documentation":"Raised by Provider::Mercury#handle_response when Mercury returns HTTP 404, meaning the requested resource does not exist on Mercury's side. In this client it can only come from get_account or get_account_transactions, where the account ID is interpolated into the URL path (URL-encoded).","triggerScenarios":"get_account(account_id) with an ID that was deleted or never existed; get_account_transactions with a typo'd or truncated UUID; an account closed at Mercury after you cached its ID; a base_url with a typo changing the effective path.","commonSituations":"Cached/stale account IDs synced earlier and the account was later closed; ID truncated by a CSV import or manual entry; copy/paste of an internal ID from Mercury's web dashboard that is not the API account UUID; trailing newline in the stored ID (partially mitigated by ERB::Util.url_encode, which will encode it into a non-matching path).","solutions":["Call get_accounts and verify the account_id is still in the returned list; if missing, remove or deactivate the local record for it.","Check the stored ID for whitespace/newlines and exact UUID formatting before passing it.","Confirm you used the API account id field (not the dashboard display ID or the last-4 digits).","Verify base_url — a wrong base URL can turn a valid path into a 404."],"exampleFix":"# before\nprovider.get_account_transactions(account_id, start_date: 30.days.ago)\n\n# after\nids = provider.get_accounts[:accounts].map { |a| a[:id] }\nif ids.include?(account_id)\n  provider.get_account_transactions(account_id, start_date: 30.days.ago)\nelse\n  Rails.logger.info \"Mercury account #{account_id} no longer exists; skipping\"\nend","handlingStrategy":"validation","validationCode":"account_id = account_id.to_s.strip\nunless account_id.match?(/\\A[0-9a-f-]{36}\\z/i) # Mercury UUID\n  raise ArgumentError, \"not a Mercury account id: #{account_id.inspect}\"\nend","typeGuard":null,"tryCatchPattern":"begin\n  provider.get_account_transactions(account_id, start_date: start)\nrescue Provider::Mercury::MercuryError => e\n  if e.error_type == :not_found\n    account.disable!(reason: \"missing_at_provider\")\n  else\n    raise\n  end\nend","preventionTips":["Validate ID shape (UUID, no whitespace) before interpolating into a path.","Reconcile cached account IDs against get_accounts each sync and retire the missing ones."],"tags":["mercury","http-404","account-id","banking-api"],"backgroundTag":"http-404-not-found","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}