{"record":{"id":"428bfd4c42bc88e3","repo":"we-promise/sure","slug":"api-secret-is-required","errorCode":null,"errorMessage":"api_secret is required","messagePattern":"api_secret is required","errorType":"validation","errorClass":"Provider::Trading212::ConfigurationError","httpStatus":null,"severity":"error","filePath":"app/models/provider/trading212.rb","lineNumber":41,"sourceCode":"  PAGE_LIMIT = 50\n\n  RETRYABLE_ERRORS = [\n    SocketError,\n    Net::OpenTimeout,\n    Net::ReadTimeout,\n    Errno::ECONNRESET,\n    Errno::ECONNREFUSED,\n    Errno::ETIMEDOUT,\n    EOFError\n  ].freeze\n\n  default_options.merge!({ timeout: 60 }.merge(httparty_ssl_options))\n\n  attr_reader :api_key, :api_secret, :environment\n\n  def initialize(api_key:, api_secret:, environment: \"live\")\n    raise ConfigurationError, \"api_key is required\" if api_key.blank?\n    raise ConfigurationError, \"api_secret is required\" if api_secret.blank?\n    raise ConfigurationError, \"Invalid environment: #{environment}\" unless %w[live demo].include?(environment.to_s)\n\n    @api_key = api_key.to_s.strip\n    @api_secret = api_secret.to_s.strip\n    @environment = environment.to_s\n  end\n\n  def fetch_account_summary\n    get(\"/equity/account/summary\")\n  end\n\n  def fetch_positions\n    get(\"/equity/positions\")\n  end\n\n  def fetch_instruments\n    get(\"/equity/metadata/instruments\")\n  end","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/trading212.rb#L23-L59","documentation":"Provider::Trading212::ConfigurationError raised in the constructor when the required api_secret keyword argument is blank. The client signs every request with Basic auth (Base64 of api_key:api_secret), so a missing secret makes authenticated calls impossible and instantiation is refused immediately.","triggerScenarios":"Calling Provider::Trading212.new(api_key: \"key\", api_secret: nil) or api_secret: \"\"; ENV[\"TRADING212_API_SECRET\"] unset; the settings UI stored the key but the secret field was left blank.","commonSituations":"Trading 212's API key/secret pair was only half-copied into environment config; secret exists in production ENV but not in CI or the developer's .env.local; a secrets rotation replaced the key but the secret propagation failed.","solutions":["Set TRADING212_API_SECRET in the environment (and .env.local for development) and pass it to the constructor","Validate both credentials up front in the caller: return a 'credentials incomplete' state to the user instead of an exception","Re-copy the secret from Trading 212's Settings > API page — key and secret must come from the same generation","If deploying, confirm the secret is present in the deployment environment's secret store, not just locally"],"exampleFix":"// before\nclient = Provider::Trading212.new(api_key: key, api_secret: params[:api_secret])\n\n// after\nsecret = params[:api_secret].presence\nreturn render_error(:api_secret_blank) if secret.nil?\n\nclient = Provider::Trading212.new(api_key: key, api_secret: secret)","handlingStrategy":"validation","validationCode":"attrs = { api_key: key, api_secret: secret }\nmissing = attrs.select { |_, v| v.blank? }.keys\nraise ArgumentError, \"Trading 212 credentials missing: #{missing.join(', ')}\" if missing.any?","typeGuard":null,"tryCatchPattern":"begin\n  Provider::Trading212.new(api_key: key, api_secret: secret)\nrescue Provider::Trading212::ConfigurationError => e\n  redirect_to provider_settings_path, alert: e.message\nend","preventionTips":["Store key and secret together in one credential record and validate both as a pair","Copy both values from the same Trading 212 API key generation dialog","Surface 'credentials incomplete' states in the UI instead of letting jobs crash"],"tags":["ruby","trading212","configuration","missing-api-secret","constructor"],"backgroundTag":"missing-api-secret","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}