{"record":{"id":"98b4c62fc400e2f3","repo":"instructure/canvas-lms","slug":"must-be-logged-in-dismiss-account-notification","errorCode":null,"errorMessage":"Must be logged in","messagePattern":"Must be logged in","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/dismiss_account_notification.rb","lineNumber":27,"sourceCode":"# the terms of the GNU Affero General Public License as published by the Free\n# Software Foundation, version 3 of the License.\n#\n# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY\n# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\n# details.\n#\n# You should have received a copy of the GNU Affero General Public License along\n# with this program. If not, see <http://www.gnu.org/licenses/>.\n#\n\nmodule Mutations\n  class DismissAccountNotification < BaseMutation\n    argument :notification_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func(\"AccountNotification\")\n\n    def resolve(input:, **)\n      user = context[:current_user]\n      raise GraphQL::ExecutionError, I18n.t(\"Must be logged in\") unless user\n\n      notification = AccountNotification.find_by(id: input[:notification_id])\n      raise GraphQL::ExecutionError, I18n.t(\"Notification not found\") unless notification\n\n      closed_notifications = user.get_preference(:closed_notifications) || []\n      closed_notifications << notification.id unless closed_notifications.include?(notification.id)\n      user.set_preference(:closed_notifications, closed_notifications)\n\n      {}\n    end\n  end\nend\n","sourceCodeStart":9,"sourceCodeEnd":40,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/dismiss_account_notification.rb#L9-L40","documentation":"GraphQL mutation DismissAccountNotification raises this when context[:current_user] is nil, meaning the GraphQL request was made without an authenticated session or access token. Canvas GraphQL mutations that act on user state refuse to run anonymously.","triggerScenarios":"Calling the dismissAccountNotification mutation with no valid session cookie or bearer token, or with an expired/revoked token so context[:current_user] resolves to nil.","commonSituations":"Unauthenticated scripts or curl calls against /api/graphql; expired Canvas session in an SPA; missing Authorization header in a service-to-service call; API token from a user who was since deleted or merged.","solutions":["Authenticate the request: pass a valid Canvas session cookie or 'Authorization: Bearer <token>' header to /api/graphql.","Verify the token is for an active, non-deleted user; regenerate it if expired (Account > Settings > Approved Integrations).","In client code, check authentication before calling the mutation (fetch /api/v1/users/self first) and redirect to login on 401.","If using masquerading, ensure the real user session is still valid."],"exampleFix":"// before\ncurl -X POST https://canvas/api/graphql -d '{\"query\":\"mutation { dismissAccountNotification(input: {notificationId: \\\"1\\\"}) { ... } }\"}'\n// after\ncurl -X POST https://canvas/api/graphql -H 'Authorization: Bearer <CANVAS_TOKEN>' -d '{\"query\":\"mutation { dismissAccountNotification(input: {notificationId: \\\"1\\\"}) { ... } }\"}'","handlingStrategy":"validation","validationCode":"const me = await fetch('/api/v1/users/self', { headers }).then(r => r.ok ? r.json() : null);\nif (!me) { redirectToLogin(); throw new Error('Not authenticated'); }","typeGuard":"const isLoggedIn = (ctx) => typeof ctx?.currentUser?.id === 'number' && ctx.currentUser.id > 0;","tryCatchPattern":"try { await client.mutate({ mutation: DISMISS, variables }); }\ncatch (e) { if (e.graphQLErrors?.some(g => g.message === 'Must be logged in')) redirectToLogin(); else throw e; }","preventionTips":["Always attach session cookie or bearer token to GraphQL requests","Check auth state before user-scoped mutations","Handle 401 globally by redirecting to login","Regenerate API tokens periodically and on staff offboarding"],"tags":["graphql","authentication","canvas-lms"],"backgroundTag":"authentication-required","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"}