instructure/canvas-lms · error · GraphQL::ExecutionError
Must be logged in
Error message
Must be logged in
What it means
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.
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.
Example fix
// before
curl -X POST https://canvas/api/graphql -d '{"query":"mutation { dismissAccountNotification(input: {notificationId: \"1\"}) { ... } }"}'
// after
curl -X POST https://canvas/api/graphql -H 'Authorization: Bearer <CANVAS_TOKEN>' -d '{"query":"mutation { dismissAccountNotification(input: {notificationId: \"1\"}) { ... } }"}' Defensive patterns
Strategy: validation
Validate before calling
const me = await fetch('/api/v1/users/self', { headers }).then(r => r.ok ? r.json() : null);
if (!me) { redirectToLogin(); throw new Error('Not authenticated'); } Type guard
const isLoggedIn = (ctx) => typeof ctx?.currentUser?.id === 'number' && ctx.currentUser.id > 0;
Try / catch
try { await client.mutate({ mutation: DISMISS, variables }); }
catch (e) { if (e.graphQLErrors?.some(g => g.message === 'Must be logged in')) redirectToLogin(); else throw e; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- and cannot be used together
- A course with that id does not exist
- Anonymous assignments cannot be hidden by section
- Anonymous assignments cannot be posted by section
- Assignments under moderation cannot be hidden before grades…
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/98b4c62fc400e2f3.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/dismiss_account_notification.rb:27
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module Mutations
class DismissAccountNotification < BaseMutation
argument :notification_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func("AccountNotification")
def resolve(input:, **)
user = context[:current_user]
raise GraphQL::ExecutionError, I18n.t("Must be logged in") unless user
notification = AccountNotification.find_by(id: input[:notification_id])
raise GraphQL::ExecutionError, I18n.t("Notification not found") unless notification
closed_notifications = user.get_preference(:closed_notifications) || []
closed_notifications << notification.id unless closed_notifications.include?(notification.id)
user.set_preference(:closed_notifications, closed_notifications)
{}
end
end
end
View on GitHub (pinned to 1c9f0bb801)