{"record":{"id":"df35f4107d29f19a","repo":"we-promise/sure","slug":"invalid-access-key","errorCode":"invalid_access_key","errorMessage":"Invalid Sophtron Access Key: #{e.message}","messagePattern":"Invalid Sophtron Access Key: #(.+?)","errorType":"exception","errorClass":"Provider::Sophtron::Error","httpStatus":null,"severity":"error","filePath":"app/models/provider/sophtron.rb","lineNumber":42,"sourceCode":"\n  attr_reader :user_id, :access_key, :base_url\n\n  def initialize(user_id, access_key, base_url: DEFAULT_BASE_URL)\n    @user_id = user_id\n    @access_key = access_key\n    @base_url = normalize_base_url(base_url)\n    super()\n  end\n\n  def auth_header_for(method, api_path)\n    auth_path = self.class.auth_path(api_path)\n    plain_key = \"#{method.to_s.upcase}\\n#{auth_path}\"\n    key_bytes = Base64.decode64(access_key.to_s)\n    raise ArgumentError, \"decoded key is empty\" if key_bytes.blank?\n    signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new(\"sha256\"), key_bytes, plain_key)\n    \"FIApiAUTH:#{user_id}:#{Base64.strict_encode64(signature)}:#{auth_path}\"\n  rescue ArgumentError => e\n    raise Error.new(\"Invalid Sophtron Access Key: #{e.message}\", :invalid_access_key)\n  end\n\n  def self.auth_path(api_path)\n    path = URI.parse(api_path.to_s).path\n    last_segment = path.to_s.split(\"/\").last.to_s\n    \"/#{last_segment}\".downcase\n  rescue URI::InvalidURIError\n    last_segment = api_path.to_s.split(\"?\").first.to_s.split(\"/\").last.to_s\n    \"/#{last_segment}\".downcase\n  end\n\n  def self.job_success?(job)\n    job = job.with_indifferent_access\n    job[:SuccessFlag] == true || job[:success_flag] == true || job[:LastStatus].to_s == \"AccountsReady\" || job[:last_status].to_s == \"AccountsReady\"\n  end\n\n  def self.job_failed?(job)\n    job = job.with_indifferent_access","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/sophtron.rb#L24-L60","documentation":"Raised by Provider::Sophtron#auth_header_for when building the FIApiAUTH HMAC-SHA256 signature fails with an ArgumentError - most commonly because Base64-decoding the stored access_key produced empty bytes ('decoded key is empty'). The Sophtron access key must be a Base64-encoded HMAC key; a blank, nil, or non-key string fails here before any HTTP request is made.","triggerScenarios":"Instantiating Provider::Sophtron with an empty/nil access_key (missing SOPHTRON_ACCESS_KEY env var), a placeholder value, or a string whose Base64 decode yields zero bytes; every request method calls auth_headers -> auth_header_for, so the first API call after construction raises.","commonSituations":"SOPHTRON_ACCESS_KEY never set in a new environment (staging/CI); key cleared during a credentials rotation but not replaced; whitespace/newline pasted with the key making decode succeed-but-wrong (then auth fails as 401 instead); key set on a different Rails env than the one running.","solutions":["Check that the access_key passed to Provider::Sophtron.new is present and non-blank in the failing environment","Re-copy the key from the Sophtron dashboard, ensuring it is the Base64 access key (not the user ID), with no trailing newline","Verify it decodes to non-empty bytes: Base64.decode64(key).length must be > 0 before constructing the client","After fixing the key, confirm end-to-end with a lightweight call - if a 401 follows, the user_id is the wrong half of the pair"],"exampleFix":"# before - constructing the client with possibly-missing config\nclient = Provider::Sophtron.new(user_id, ENV[\"SOPHTRON_ACCESS_KEY\"])\n\n# after - fail fast on an unusable key\nkey = ENV[\"SOPHTRON_ACCESS_KEY\"].to_s\nraise ArgumentError, \"SOPHTRON_ACCESS_KEY missing or empty\" if Base64.decode64(key).bytesize.zero?\nclient = Provider::Sophtron.new(user_id, key)","handlingStrategy":"validation","validationCode":"key = credentials.access_key.to_s\nif key.blank? || Base64.decode64(key).bytesize.zero?\n  raise ArgumentError, \"Sophtron access key is missing or decodes empty - check SOPHTRON_ACCESS_KEY\"\nend\nclient = Provider::Sophtron.new(credentials.user_id, key)","typeGuard":"def usable_sophtron_key?(key)\n  decoded = Base64.decode64(key.to_s)\n  !key.to_s.blank? && decoded.bytesize.positive?\nend","tryCatchPattern":"begin\n  client.get_accounts\nrescue Provider::Sophtron::Error => e\n  raise ConfigError, \"Re-link Sophtron credentials\" if e.error_type == :invalid_access_key\n  raise\nend","preventionTips":["Assert at boot that SOPHTRON_ACCESS_KEY decodes to non-empty bytes","Strip whitespace/newlines when storing pasted keys","Fail configuration checks during deploy, not at the first user-triggered sync"],"tags":["sophtron","hmac","access-key","credentials","base64"],"backgroundTag":"invalid-api-key","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}