we-promise/sure · error · WiseItem::Syncer::SafeSyncError
Sync failed. Please try again or contact support.
Error message
Sync failed. Please try again or contact support.
What it means
Raised as SafeSyncError by WiseItem::Syncer (app/models/wise_item/syncer.rb:71) after ANY exception escapes the Wise sync body. The syncer logs the original class/message and backtrace, reports it to Sentry tagged with wise_item_id, records a health-stats entry (category sync_error) using a user-safe i18n message — 'credentials_invalid' for Provider::Wise::WiseError with error_type unauthorized/access_forbidden, otherwise the generic 'Sync failed. Please try again or contact support.' — and then re-raises that sanitized message. The original error is intentionally hidden from users.
Source
Thrown at app/models/wise_item/syncer.rb:71
)
sync_errors.concat(result_failure_errors(schedule_results, category: :account_sync_error, message_key: :account_sync_failed))
# Phase 5: Collect transaction statistics
account_ids = wise_item.wise_accounts
.joins(:account_provider)
.includes(account_provider: :account)
.filter_map { |wa| wa.current_account&.id }
collect_transaction_stats(sync, account_ids: account_ids, source: "wise")
end
collect_health_stats(sync, errors: sync_errors.presence)
rescue => e
safe_message = user_safe_error_message(e)
Rails.logger.error "WiseItem::Syncer - sync failed for item #{wise_item.id}: #{e.class} - #{e.message}"
Rails.logger.error Array(e.backtrace).first(10).join("\n")
Sentry.capture_exception(e) { |s| s.set_tags(wise_item_id: wise_item.id) }
collect_health_stats(sync, errors: [ { message: safe_message, category: "sync_error" } ])
raise SafeSyncError, safe_message
end
def perform_post_sync
# no-op
end
private
def update_status(sync, key, **options)
return unless sync.respond_to?(:status_text)
sync.update!(status_text: I18n.t("wise_items.syncer.#{key}", **options))
end
def collect_wise_setup_stats(sync, total_count:, linked_count:, unlinked_count:)
return unless sync.respond_to?(:sync_stats)
merge_sync_stats(sync, {View on GitHub (pinned to e69894adb9)
Solutions
- Look up the real cause in Sentry (tag wise_item_id) or the Rails log lines 'WiseItem::Syncer - sync failed for item <id>' — the SafeSyncError text alone is deliberately uninformative.
- If the user-facing message is the credentials-invalid variant, re-authorize/re-enter the Wise API token on the item and sync again.
- For transient network/API failures, retry the sync once after a short delay.
- If cause is unclear, reproduce with the item's credentials via Provider::Wise directly to separate auth problems from data problems.
Example fix
// before
begin
WiseItem::Syncer.new(item).perform_sync(sync)
rescue => e
retry # loops on the same persistent failure
end
// after
begin
WiseItem::Syncer.new(item).perform_sync(sync)
rescue SafeSyncError => e
Rails.logger.warn("wise sync failed safely: #{e.message}") # real cause is in Sentry
end Defensive patterns
Strategy: try-catch
Try / catch
begin WiseItem::Syncer.new(item).perform_sync(sync) rescue SafeSyncError => e # e.message is sanitized/user-safe; real cause is in Sentry (tag wise_item_id) retry if transient?(e.message) # only for known-transient messages end
Prevention
- Never parse the SafeSyncError message to infer cause — correlate via Sentry wise_item_id tag or the Rails log lines instead.
- Alert on the credentials_invalid variant specifically: it needs user re-auth, not a retry.
- Bound retries; a persistent underlying failure will keep re-raising the same safe message.
When it happens
Trigger: Any failure inside perform_sync: Wise API 5xx/timeout, expired or revoked API tokens (WiseError unauthorized), malformed payloads, or a bug in the import pipeline. The SafeSyncError you catch is a wrapper; the real cause is only in logs/Sentry.
Common situations: Expired Wise API token triggering the unauthorized path; transient Wise outage surfacing as the generic message; users retrying sync repeatedly without effect because the underlying cause (e.g. revoked token) persists.
Related errors
- Sync failed. Please try again or contact support.
- record_not_found
- Trading 212 API key is missing.
- This property data provider is not configured.
- {response.error.message}
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/53e45d33618673c2.
Report an issue: GitHub.