instructure/canvas-lms · error · FailedSnsInteraction

Unable to create or reassign SNS endpoint for access_token #

Error message

Unable to create or reassign SNS endpoint for access_token #{access_token.global_id}

What it means

NotificationEndpoint#create_platform_endpoint manages AWS SNS platform endpoints for push-notification access tokens. If SNS creation/assignment keeps failing and the code has already retried once (retried == true), it raises FailedSnsInteraction with this message including the access token's global id. It means Canvas could not create or reassign the SNS endpoint after the documented retry.

Solutions

  1. Check AWS credentials and IAM policy for the SNS actions used (CreatePlatformEndpoint, CreatePlatformApplication, SetEndpointAttributes) in the configured region.
  2. Verify the SNS platform application ARN configured for the account is valid and exists in the current region.
  3. Inspect the underlying exception (logged via Canvas::Errors.capture_exception) to see the exact AWS error, e.g. invalid device token — re-register the device to get a fresh token.
  4. Delete stale NotificationEndpoint/AccessToken rows and let the client re-register push notifications.
  5. Check for AWS throttling or SNS outages if the failure is intermittent.

Example fix

// before
# relies on implicit retry; fails with FailedSnsInteraction after 1 retry
NotificationEndpoint.create_or_update(access_token, token: device_token)
// after
begin
  NotificationEndpoint.create_or_update(access_token, token: device_token)
rescue FailedSnsInteraction => e
  Rails.logger.warn("SNS endpoint failed for token #{access_token.global_id}: #{e.cause&.message}")
  access_token.notification_endpoints.destroy_all # clear stale endpoint, allow re-register
  raise
end
Defensive patterns

Strategy: retry

Validate before calling

return unless access_token.present? && device_token.present?
return unless Aws.config[:region] == ENV['SNS_REGION']

Try / catch

begin
  NotificationEndpoint.create_or_update(access_token, token: device_token)
rescue FailedSnsInteraction => e
  Canvas::Errors.capture_exception(:push_notifications, e, :error)
  # surface a re-register flow to the mobile client
end

Prevention

When it happens

Trigger: Registering a mobile push endpoint when AWS SNS repeatedly fails (invalid device token, IAM permissions missing sns:CreatePlatformEndpoint/sns:CreatePlatformApplication, wrong platform application ARN) so the first retry also fails and retried is already true.

Common situations: Misconfigured SNS credentials/region or expired AWS keys in the environment; stale/invalid APNs or FCM device tokens; deleted or recreated SNS platform application leaving tokens orphaned; AWS throttling/region mismatch.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/286cdc4f282a9f25. Report an issue: GitHub.

Appendix: source

Thrown at app/models/notification_endpoint.rb:110

      self.arn = $1
      # steal the endpoint by setting the access token
      endpoint_updated = false
      begin
        sns_client.set_endpoint_attributes(
          endpoint_arn: arn,
          attributes: { "CustomUserData" => access_token.global_id.to_s }
        )
        endpoint_updated = true
      rescue Aws::SNS::Errors::NotFound => ex
        # there's a race condition if the endpoint we JUST found
        # and are trying to update gets deleted by a different
        # request in the same moment.  In this case we should
        # try to create again, since the blocking endpoint is gone,
        # but only once since if it's cyclical something strange
        # is happening.
        endpoint_updated = false
        if retried
          raise FailedSnsInteraction, "Unable to create or reassign SNS endpoint for access_token #{access_token.global_id}"
        end

        retried = true
        Canvas::Errors.capture_exception(:push_notifications, ex, :info)
      end
      retry unless endpoint_updated
    end
  end

  def delete_platform_endpoint
    return unless endpoint_exists? && own_endpoint?

    sns_client.delete_endpoint(endpoint_arn: arn)
  end
end

View on GitHub (pinned to 1c9f0bb801)