{"record":{"id":"60f0af5c6b027443","repo":"we-promise/sure","slug":"trading-212-api-key-is-missing","errorCode":null,"errorMessage":"Trading 212 API key is missing.","messagePattern":"Trading 212 API key is missing\\.","errorType":"exception","errorClass":"Provider::Trading212::ConfigurationError","httpStatus":null,"severity":"error","filePath":"app/models/trading212_item/syncer.rb","lineNumber":14,"sourceCode":"class Trading212Item::Syncer\n  include SyncStats::Collector\n\n  attr_reader :trading212_item\n\n  def initialize(trading212_item)\n    @trading212_item = trading212_item\n  end\n\n  def perform_sync(sync)\n    sync.update!(status_text: I18n.t(\"trading212_items.sync.status.checking_credentials\")) if sync.respond_to?(:status_text)\n    unless trading212_item.credentials_configured?\n      trading212_item.update!(status: :requires_update)\n      raise Provider::Trading212::ConfigurationError, \"Trading 212 API key is missing.\"\n    end\n\n    sync.update!(status_text: I18n.t(\"trading212_items.sync.status.importing_account\")) if sync.respond_to?(:status_text)\n    trading212_item.import_latest_data\n\n    sync.update!(status_text: I18n.t(\"trading212_items.sync.status.checking_configuration\")) if sync.respond_to?(:status_text)\n    collect_setup_stats(sync, provider_accounts: trading212_item.trading212_accounts.to_a)\n\n    unlinked_accounts = trading212_item.trading212_accounts.left_joins(:account_provider).where(account_providers: { id: nil })\n    linked_accounts = trading212_item.trading212_accounts.joins(:account).merge(Account.visible)\n\n    if unlinked_accounts.any?\n      trading212_item.update!(pending_account_setup: true)\n      sync.update!(status_text: I18n.t(\"trading212_items.sync.status.accounts_need_setup\", count: unlinked_accounts.count)) if sync.respond_to?(:status_text)\n    else\n      trading212_item.update!(pending_account_setup: false)\n    end\n","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/trading212_item/syncer.rb#L1-L32","documentation":"Raised as Provider::Trading212::ConfigurationError by Trading212Item::Syncer#perform_sync (app/models/trading212_item/syncer.rb:14) when trading212_item.credentials_configured? is false (blank api_key or api_secret). The syncer first flips the item's status enum to requires_update, then raises, so the item is visibly flagged in the UI. This is the sync-pipeline counterpart of the 'provider is not configured' error raised deeper in import_latest_data.","triggerScenarios":"A scheduled or manual sync (Syncable#perform_sync) running against a Trading212Item with a blank api_key or api_secret; items whose credentials were cleared or never persisted; running the syncer from console on a half-configured test record.","commonSituations":"User deletes/rotates their Trading 212 key without updating the item; encryption-key mismatch making api_key read as nil; CI running syncer specs on fixtures that lack the encrypted credential attributes.","solutions":["Update the item's credentials (api_key + api_secret) so credentials_configured? is true, then re-run the sync.","In calling code, rescue Provider::Trading212::ConfigurationError specifically and surface an 'update your credentials' action instead of a generic failure — the item is already marked requires_update.","Exclude items without credentials from sync scheduling using the syncable scope (active.where.not(api_key: [nil, ''])).","For specs, stub credentials_configured? or provide both attributes before invoking perform_sync."],"exampleFix":"// before\nTrading212Item::Syncer.new(item).perform_sync(sync) # ConfigurationError\n\n// after\nbegin\n  Trading212Item::Syncer.new(item).perform_sync(sync)\nrescue Provider::Trading212::ConfigurationError\n  # item already flagged requires_update; prompt user to re-enter credentials\nend","handlingStrategy":"try-catch","validationCode":"Trading212Item::Syncer.new(item).perform_sync(sync) if item.credentials_configured?","typeGuard":"item.credentials_configured? && !item.scheduled_for_deletion?","tryCatchPattern":"begin\n  Trading212Item::Syncer.new(item).perform_sync(sync)\nrescue Provider::Trading212::ConfigurationError\n  # item already marked requires_update; notify user to re-enter API credentials\nrescue => e\n  # unexpected failure: log + report\nend","preventionTips":["Rescue ConfigurationError separately from generic sync errors — it is a user-actionable state.","Only enqueue syncs for items matching the syncable scope.","In tests, always provide api_key/api_secret or stub credentials_configured? before perform_sync."],"tags":["rails","trading212","sync","configuration","missing-credentials"],"backgroundTag":"missing-credentials","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}