instructure/canvas-lms · error · GraphQL::ExecutionError
not found
Error message
not found
What it means
CreateSubmissionDraft#resolve wraps its work in a rescue for ActiveRecord::RecordNotFound and re-raises it as GraphQL::ExecutionError "not found". It fires when a record required to build the submission draft (typically the Submission via the given id, or an attachment/media record) cannot be found for the current context.
Solutions
- Verify the submissionId exists (Submission.active.find) and belongs to the current user before calling the mutation.
- Confirm any referenced file ids / media ids still exist.
- Check that the assignment and its submissions were not deleted.
- Read server logs for the RecordNotFound backtrace to identify the exact missing model.
Example fix
// before
createSubmissionDraft(input: { submissionId: staleId, ... })
// after
const sub = await apollo.query(gql`query($id: ID!){ submission(id:$id){ id } }`, { id: freshId })
if (sub) createSubmissionDraft(input: { submissionId: freshId, ... }) Defensive patterns
Strategy: try-catch
Validate before calling
const { data } = await client.query(gql`query($id: ID!){ submission(id: $id){ id } }`, { id })
if (!data?.submission) throw new SkipError('submission missing') Type guard
function canDraft(submission) { return submission != null && !submission.deleted; } Try / catch
try {
await createSubmissionDraft(input)
} catch (e) {
if (e.graphQLErrors?.some(g => g.message === 'not found')) {
await refetchAssignment(); notifyUserStale();
} else throw e;
} Prevention
- Refetch submission state before drafting; never reuse long-lived ids.
- Validate referenced file/media ids still exist before saving the draft.
- Scope ids to the current course/user context.
When it happens
Trigger: Calling createSubmissionDraft with a submissionId that does not exist, was soft-deleted, or is not visible to current_user; referencing file/media ids that were deleted before save!; cross-context ids.
Common situations: Stale client cache after the assignment/submission was deleted; student unenrolled so the submission lookup fails; attempt/submission state purged; using an id from another course or shard.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- A course with that id does not exist
- ActiveRecord::RecordNotFound
- Allocation rule not found
- An assignment with that id does not exist
- Assignment not found
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/e0077d2a777676c4.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/create_submission_draft.rb:92
submission_draft.resource_link_lookup_uuid = input[:resource_link_lookup_uuid]
when "media_recording"
submission_draft.media_object_id = input[:media_id]
when "online_text_entry"
submission_draft.body = input[:body]
when "online_upload"
file_ids = (input[:file_ids] || []).compact.uniq
attachments = get_and_verify_attachments!(file_ids)
verify_allowed_extensions!(submission.assignment, attachments)
submission_draft.attachments = attachments
when "online_url"
submission_draft.url = input[:url]
end
submission_draft.save!
{ submission_draft: }
rescue ActiveRecord::RecordNotFound
raise GraphQL::ExecutionError, "not found"
rescue ActiveRecord::RecordInvalid => e
# activerecord validation is not robust to race condition
# multiple concurrent requests may penetrate activerecord validations
# and save dup records for a combination of submission_id and attempt
# If it happened, following saves will be blocked by activerecord validation
# Ideally, an unique index should be defined, but with many existing records,
# creating unique index may fail without cleaning data first.
if submission_draft.present?
submission_drafts = SubmissionDraft.where(
submission: submission_draft.submission,
submission_attempt: submission_draft.submission_attempt
)
if submission_drafts.count > 1 && !@retried
@retried = true
submission_drafts.where.not(id: submission_draft.id).destroy_all
retry
end
endView on GitHub (pinned to 1c9f0bb801)