{"record":{"id":"fd723c8ab9035a0c","repo":"we-promise/sure","slug":"unauthorized-check-your-api-key-and-secret","errorCode":null,"errorMessage":"Unauthorized - check your API key and secret","messagePattern":"Unauthorized - check your API key and secret","errorType":"exception","errorClass":"Provider::Coinbase::AuthenticationError","httpStatus":401,"severity":"error","filePath":"app/models/provider/coinbase.rb","lineNumber":201,"sourceCode":"      \"#{message}.#{encoded_signature}\"\n    end\n\n    def auth_headers(method, path)\n      {\n        \"Authorization\" => \"Bearer #{generate_jwt(method, path)}\",\n        \"Content-Type\" => \"application/json\"\n      }\n    end\n\n    def handle_response(response)\n      parsed = response.parsed_response\n\n      case response.code\n      when 200..299\n        parsed.is_a?(Hash) ? parsed : { \"data\" => parsed }\n      when 401\n        error_msg = extract_error_message(parsed) || \"Unauthorized - check your API key and secret\"\n        raise AuthenticationError, error_msg\n      when 429\n        raise RateLimitError, \"Rate limit exceeded\"\n      else\n        error_msg = extract_error_message(parsed) || \"API error: #{response.code}\"\n        raise ApiError, error_msg\n      end\n    end\n\n    def extract_error_message(parsed)\n      return parsed if parsed.is_a?(String)\n      return nil unless parsed.is_a?(Hash)\n\n      parsed.dig(\"errors\", 0, \"message\") || parsed[\"error\"] || parsed[\"message\"]\n    end\nend\n","sourceCodeStart":183,"sourceCodeEnd":217,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/coinbase.rb#L183-L217","documentation":"Raised by Provider::Coinbase#handle_response when the Coinbase API returns HTTP 401. The message is extract_error_message(parsed) — which reads parsed.dig(\"errors\", 0, \"message\") — or the fallback \"Unauthorized - check your API key and secret\". Coinbase CDP keys fail auth when the key/secret pair is wrong, the key was revoked, or it belongs to a different environment (sandbox vs production).","triggerScenarios":"Any Coinbase request with a revoked, expired, or mistyped CDP API key/secret; using a sandbox key against api.coinbase.com (or vice versa); whitespace or truncated secrets pasted from the Coinbase developer console; keys deleted when the CDP project was removed.","commonSituations":"Credential rotation on the Coinbase developer console without updating the stored secret; env vars overridden per-environment (test key in prod); secrets mangled by shell interpolation or YAML formatting; team members revoking old keys not knowing the app still uses them.","solutions":["Verify the stored key and secret match an active key in the Coinbase Developer Platform (correct project and environment).","Re-copy both values (watch for trailing newlines/spaces) and update the stored credential, then retry.","Confirm you are hitting the right base URL for the key type (sandbox vs production).","If auth still fails, create a fresh key/secret pair and swap it in.","Handle Provider::Coinbase::AuthenticationError by flagging the connection for re-auth instead of dead-looping retries."],"exampleFix":"# before\nprovider = Provider::Coinbase.new(api_key: key, api_secret: secret)\nbalance = provider.get_accounts\n\n# after\nbegin\n  provider = Provider::Coinbase.new(api_key: key.strip, api_secret: secret.strip)\n  balance = provider.get_accounts\nrescue Provider::Coinbase::AuthenticationError => e\n  coinbase_account.update!(status: :reauth_required)\n  Rails.logger.warn(\"Coinbase auth failed: #{e.message}\")\nend","handlingStrategy":"try-catch","validationCode":"raise ArgumentError, \"Coinbase credentials incomplete\" if api_key.to_s.strip.empty? || api_secret.to_s.strip.empty?","typeGuard":"def coinbase_auth_error?(err)\n  err.is_a?(Provider::Coinbase::AuthenticationError)\nend","tryCatchPattern":"begin\n  accounts = provider.get_accounts\nrescue Provider::Coinbase::AuthenticationError => e\n  connection.update!(status: :reauth_required)\n  Rails.logger.warn(\"Coinbase auth failed: #{e.message}\")\nend","preventionTips":["Strip whitespace when storing key/secret; YAML/shell mangling is a top cause.","Keep sandbox and production keys in distinct, clearly named credentials — never mix environments.","When rotating keys in the Coinbase console, update the stored credential in the same change.","Never retry AuthenticationError; surface re-auth to the user."],"tags":["coinbase","authentication","http-401","api-key","crypto"],"backgroundTag":"http-401-unauthorized","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}