{"record":{"id":"73d2e38416b2630b","repo":"we-promise/sure","slug":"realie-matched-a-property-in-a-different-city-or-z","errorCode":null,"errorMessage":"Realie matched a property in a different city or ZIP code. Please double-check the address.","messagePattern":"Realie matched a property in a different city or ZIP code\\. Please double-check the address\\.","errorType":"exception","errorClass":"Provider::Realie::Error","httpStatus":null,"severity":"warning","filePath":"app/models/provider/realie.rb","lineNumber":68,"sourceCode":"\n      # The address lookup endpoint accepts only the street address and\n      # 2-letter state code — filtering by city additionally requires a\n      # county, which the property address form doesn't collect.\n      response = client.get(\"#{base_url}/api/public/property/address/\") do |req|\n        req.params[\"address\"] = line1.to_s.strip\n        req.params[\"state\"] = region.to_s.strip.upcase\n      end\n\n      parsed = JSON.parse(response.body)\n      records = parsed[\"property\"]\n      records = [ records ] unless records.is_a?(Array)\n      records = records.reject(&:blank?)\n      raise Error.new(I18n.t(\"providers.realie.errors.no_property\")) if records.empty?\n\n      # A street+state query can return several candidates across different\n      # cities; pick the first one consistent with the entered city/ZIP.\n      record = records.find { |candidate| location_match?(candidate, locality: locality, postal_code: postal_code) }\n      raise Error.new(I18n.t(\"providers.realie.errors.location_mismatch\")) if record.nil?\n\n      # Realie returns modelValue: 0 when it couldn't produce an AVM\n      # estimate, so zero counts as absent and falls back to the assessed\n      # total market value.\n      valuation = [ record[\"modelValue\"], record[\"totalMarketValue\"] ].find { |value| value.present? && value.to_d.positive? }\n      raise Error.new(I18n.t(\"providers.realie.errors.no_valuation\")) if valuation.nil?\n\n      PropertyValuation.new(\n        valuation: BigDecimal(valuation.to_s),\n        currency: \"USD\",\n        property_type: subtype_for_use_code(record[\"useCode\"]),\n        year_built: record[\"yearBuilt\"],\n        area_value: record[\"buildingArea\"],\n        area_unit: \"sqft\"\n      )\n    end\n  end\n","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/realie.rb#L50-L86","documentation":"Raised in Provider::Realie#fetch_property_valuation when Realie returned candidate properties but none satisfied location_match?: each candidate's returned city/zipCode contradicted the entered locality/postal_code (case-insensitive city compare; ZIP compared on first 5 chars; a candidate matches when nothing contradicts). Because the API query matches on street + state ONLY (city filtering would additionally require a county the form doesn't collect), common street names resolve to properties in other cities — this error is the guard against syncing the wrong property's valuation.","triggerScenarios":"'100 Main St' + 'TX' returning Main Streets in Dallas, Austin, and Houston while the user entered Houston; entered city or ZIP with a typo so every candidate 'contradicts'; user swapped city and ZIP fields; returned record has a different ZIP formatting (e.g. ZIP+4 handled via first(5)) that still mismatches on city.","commonSituations":"Apartment/duplex addresses where the entered city is actually the postal-service city but the record carries the incorporated city; users entering a neighborhood name (e.g. 'Brooklyn') where the record says 'New York'; stale ZIP from a previous address; addresses near city boundaries.","solutions":["Have the user double-check city and ZIP against how the USPS formats the address (USPS lookup), then retry.","Prefer ZIP over city when they conflict: pass the verified postal_code and treat locality as optional (location_match? only fails on contradiction — a blank entered city never mismatches).","If multiple candidates are routine for your data, consider surfacing the candidate list for user selection instead of failing.","Validate the ZIP is 5 digits and the state is a valid 2-letter code before the call."],"exampleFix":"# before\nrealie.fetch_property_valuation(\n  line1: \"100 Main St\", locality: \"Brooklyn\", region: \"NY\", postal_code: \"11201\"\n) # record city 'New York' contradicts -> location_mismatch\n\n# after\n# USPS-style locality matches the assessor record's city\nrealie.fetch_property_valuation(\n  line1: \"100 Main St\", locality: usps_city_for(address), region: \"NY\", postal_code: address.zip.first(5)\n)","handlingStrategy":"validation","validationCode":"# pre-verify the entered city/ZIP pair the way location_match? will judge it\nzip = postal_code.to_s.strip.first(5)\nraise ArgumentError, \"5-digit ZIP required\" unless zip.match?(/\\A\\d{5}\\z/)\n# USPS canonicalize so 'Brooklyn' becomes the assessor's city when needed\ncity = UspsLookup.city_for(zip) || locality.to_s.strip","typeGuard":null,"tryCatchPattern":"begin\n  valuation = realie.fetch_property_valuation(line1:, locality:, region:, postal_code:)\nrescue Provider::Realie::Error => e\n  raise unless e.message == I18n.t(\"providers.realie.errors.location_mismatch\")\n  # retry once with locality blank: ZIP alone still disambiguates\n  realie.fetch_property_valuation(line1:, locality: nil, region:, postal_code:)\nend","preventionTips":["Canonicalize city against USPS for the entered ZIP before sending (neighborhood names fail the compare).","Remember the API matches street+state only, so always pass the ZIP to keep the disambiguation.","A blank locality never mismatches — omit the city rather than sending a guessed one."],"tags":["realie","address-matching","city-zip-mismatch","property-valuation","data-quality"],"backgroundTag":"address-mismatch","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}