{"record":{"id":"286cdc4f282a9f25","repo":"instructure/canvas-lms","slug":"unable-to-create-or-reassign-sns-endpoint-for-access-token","errorCode":null,"errorMessage":"Unable to create or reassign SNS endpoint for access_token #{access_token.global_id}","messagePattern":"Unable to create or reassign SNS endpoint for access_token #(.+?)","errorType":"exception","errorClass":"FailedSnsInteraction","httpStatus":null,"severity":"error","filePath":"app/models/notification_endpoint.rb","lineNumber":110,"sourceCode":"      self.arn = $1\n      # steal the endpoint by setting the access token\n      endpoint_updated = false\n      begin\n        sns_client.set_endpoint_attributes(\n          endpoint_arn: arn,\n          attributes: { \"CustomUserData\" => access_token.global_id.to_s }\n        )\n        endpoint_updated = true\n      rescue Aws::SNS::Errors::NotFound => ex\n        # there's a race condition if the endpoint we JUST found\n        # and are trying to update gets deleted by a different\n        # request in the same moment.  In this case we should\n        # try to create again, since the blocking endpoint is gone,\n        # but only once since if it's cyclical something strange\n        # is happening.\n        endpoint_updated = false\n        if retried\n          raise FailedSnsInteraction, \"Unable to create or reassign SNS endpoint for access_token #{access_token.global_id}\"\n        end\n\n        retried = true\n        Canvas::Errors.capture_exception(:push_notifications, ex, :info)\n      end\n      retry unless endpoint_updated\n    end\n  end\n\n  def delete_platform_endpoint\n    return unless endpoint_exists? && own_endpoint?\n\n    sns_client.delete_endpoint(endpoint_arn: arn)\n  end\nend\n","sourceCodeStart":92,"sourceCodeEnd":126,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/notification_endpoint.rb#L92-L126","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check AWS credentials and IAM policy for the SNS actions used (CreatePlatformEndpoint, CreatePlatformApplication, SetEndpointAttributes) in the configured region.","Verify the SNS platform application ARN configured for the account is valid and exists in the current region.","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.","Delete stale NotificationEndpoint/AccessToken rows and let the client re-register push notifications.","Check for AWS throttling or SNS outages if the failure is intermittent."],"exampleFix":"// before\n# relies on implicit retry; fails with FailedSnsInteraction after 1 retry\nNotificationEndpoint.create_or_update(access_token, token: device_token)\n// after\nbegin\n  NotificationEndpoint.create_or_update(access_token, token: device_token)\nrescue FailedSnsInteraction => e\n  Rails.logger.warn(\"SNS endpoint failed for token #{access_token.global_id}: #{e.cause&.message}\")\n  access_token.notification_endpoints.destroy_all # clear stale endpoint, allow re-register\n  raise\nend","handlingStrategy":"retry","validationCode":"return unless access_token.present? && device_token.present?\nreturn unless Aws.config[:region] == ENV['SNS_REGION']","typeGuard":null,"tryCatchPattern":"begin\n  NotificationEndpoint.create_or_update(access_token, token: device_token)\nrescue FailedSnsInteraction => e\n  Canvas::Errors.capture_exception(:push_notifications, e, :error)\n  # surface a re-register flow to the mobile client\nend","preventionTips":["Validate SNS credentials, region, and platform application ARN at deploy time","Request fresh device tokens from clients when SNS reports InvalidParameter","Grant IAM sns:CreatePlatformEndpoint and related actions to the app role","Monitor the captured cause exception for recurring AWS error codes"],"tags":["ruby","aws","sns","push-notifications"],"backgroundTag":"api-request-failed","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}