{"record":{"id":"34cc887596fa06c3","repo":"we-promise/sure","slug":"not-found-34cc88","errorCode":"not_found","errorMessage":"Sophtron resource not found","messagePattern":"Sophtron resource not found","errorType":"exception","errorClass":"Provider::Sophtron::Error","httpStatus":404,"severity":"error","filePath":"app/models/provider/sophtron.rb","lineNumber":363,"sourceCode":"      }\n    end\n\n    def handle_response(response, parse_json: true)\n      body = response.body.to_s\n\n      case response.code.to_i\n      when 200, 201, 204\n        return {} if body.strip.blank?\n\n        parse_json ? JSON.parse(body, symbolize_names: true) : parse_optional_json(body)\n      when 400\n        raise Error.new(\"Bad request to Sophtron API: #{body}\", :bad_request, details: body)\n      when 401\n        raise Error.new(\"Invalid Sophtron User ID or Access Key\", :unauthorized, details: body)\n      when 403\n        raise Error.new(\"Access forbidden by Sophtron\", :access_forbidden, details: body)\n      when 404\n        raise Error.new(\"Sophtron resource not found\", :not_found, details: body)\n      when 429\n        raise Error.new(\"Sophtron rate limit exceeded. Please try again later.\", :rate_limited, details: body)\n      else\n        raise Error.new(\n          \"Sophtron API request failed: #{response.code} #{response.message} - #{body}\",\n          :fetch_failed,\n          details: body\n        )\n      end\n    rescue JSON::ParserError => e\n      raise Error.new(\"Invalid JSON response from Sophtron API: #{e.message}\", :invalid_response, details: body)\n    end\n\n    def parse_optional_json(body)\n      JSON.parse(body, symbolize_names: true)\n    rescue JSON::ParserError\n      body\n    end","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/sophtron.rb#L345-L381","documentation":"Provider::Sophtron raises this Error with error_type=:not_found when Sophtron responds HTTP 404. It signals that the resource referenced by the request path (job ID, institution ID, customer ID, account ID) does not exist under the authenticated API user.","triggerScenarios":"Polling a Sophtron job ID that already expired or belongs to another customer; GET accounts/transactions for a deleted SophtronAccount; referencing an institution_id typo'd or removed from Sophtron's catalog; V2 customer endpoints after the customer record was deleted server-side.","commonSituations":"Resuming an old sync from a stale job ID persisted on SophtronAccount.current_job; a bank connection deleted from the Sophtron side while the local SophtronAccount still links to it; environment mismatch where IDs created in sandbox are requested from production.","solutions":["Re-enumerate from a parent resource (list customers/accounts) instead of reusing a persisted ID","If the ID came from a stored SophtronAccount, mark that account for re-linking or deletion and start a new add-institution job","Verify the ID was obtained from the same Sophtron environment/base_url the client is configured with","Check e.details body - Sophtron usually echoes which resource was missing"],"exampleFix":"# before\njob = provider.get_job(stale_job_id)\n\n# after - tolerate vanished jobs\nbegin\n  job = provider.get_job(stale_job_id)\nrescue Provider::Sophtron::Error => e\n  raise if e.error_type != :not_found\n  job = provider.start_add_institution_job(institution_id) # fresh job\nend","handlingStrategy":"try-catch","validationCode":"# Confirm the referenced job/account id came from a live listing\nids = provider.get_accounts(customer_id).map { |a| a[:id] }\nraise ArgumentError, \"unknown account\" unless ids.include?(target_id)","typeGuard":"def sophtron_not_found?(err)\n  err.is_a?(Provider::Sophtron::Error) && err.error_type == :not_found\nend","tryCatchPattern":"begin\n  provider.get_job(job_id)\nrescue Provider::Sophtron::Error => e\n  raise unless e.error_type == :not_found\n  job_id = provider.start_add_institution_job(institution_id).fetch(:id) # re-bootstrap\nend","preventionTips":["Treat persisted Sophtron job/account IDs as cacheable but expirable - re-enumerate on 404","Never reuse IDs across Sophtron environments","Expire stored job IDs after a TTL so stale polling cannot continue for days"],"tags":["sophtron","http-404","resource-not-found","aggregator"],"backgroundTag":"resource-not-found","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}