{"record":{"id":"4e4e8075b242c870","repo":"instructure/canvas-lms","slug":"no-attachments-found-for-the-following-ids-ids","errorCode":null,"errorMessage":"No attachments found for the following ids: %{ids}","messagePattern":"No attachments found for the following ids: %(.+?)","errorType":"exception","errorClass":"SubmissionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/create_submission_draft.rb","lineNumber":151,"sourceCode":"      shard.activate do\n        valid_attachment_ids = get_attachment_ids(attachments.map(&:id))\n        validate_file_ids!(attachments, valid_attachment_ids)\n        return_attachments += Attachment.active.where(id: valid_attachment_ids)\n      end\n    end\n\n    return_attachments.each do |attachment|\n      verify_authorized_action!(attachment, :read)\n    end\n    return_attachments\n  end\n\n  def validate_file_ids!(file_id_attachments, valid_attachment_ids)\n    file_ids = file_id_attachments.pluck(:id).map(&:to_s)\n    file_ids.each do |file_id|\n      next if valid_attachment_ids.include?(file_id)\n\n      raise SubmissionError, I18n.t(\n        \"No attachments found for the following ids: %{ids}\",\n        { ids: file_ids - valid_attachment_ids }\n      )\n    end\n  end\n\n  # TODO: move this into the model\n  def verify_allowed_extensions!(assignment, attachments)\n    return if assignment.allowed_extensions.blank?\n\n    raise SubmissionError, I18n.t(\"Invalid file type\") unless attachments.all? do |attachment|\n      attachment_extension = attachment.after_extension || \"\"\n      assignment.allowed_extensions.include?(attachment_extension.downcase)\n    end\n  end\n\n  def get_attachment_ids(file_ids)\n    return [] if file_ids.empty?","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/create_submission_draft.rb#L133-L169","documentation":"CreateSubmissionDraft#validate_file_ids! checks each submitted file id against the set of attachment ids the current user is allowed to submit (their own files, group files, etc.). If any file id is not in the valid set, it raises SubmissionError listing the invalid ids. This prevents users from attaching files they do not own or that do not exist.","triggerScenarios":"Passing fileIds to createSubmissionDraft where at least one id is not in valid_attachment_ids: file deleted, file owned by another user, wrong ids, or an id string with wrong format (numeric vs relay id mismatch).","commonSituations":"Client uploaded files then re-uploaded/deduplicated and kept stale ids; submitting on behalf of a group with another member's file ids that aren't in the allowed set; ids from a different user's session; hard-coded test ids.","solutions":["Re-query the current user's available files and submit only ids returned there.","Remove the offending ids named in the error message before retrying.","Re-upload the deleted file and use the new attachment id.","Ensure you pass legacy numeric ids (or the expected format) consistently, not relay/global ids the validator can't match."],"exampleFix":"// before\nfileIds: [123, 456] // 456 deleted\n// after\nfileIds: [123, 789] // re-upload and use fresh Attachment id","handlingStrategy":"validation","validationCode":"function assertValidFileIds(submittedIds, allowedIds) {\n  const bad = submittedIds.filter(id => !allowedIds.includes(String(id)));\n  if (bad.length) throw new SkipError(`invalid file ids: ${bad.join(',')}`);\n}","typeGuard":"function allOwned(attachments, userId) { return attachments.every(a => a?.uploaderId === userId || a?.ownerId === userId); }","tryCatchPattern":"try {\n  await createSubmissionDraft({ fileIds })\n} catch (e) {\n  if (e.message.includes('No attachments found')) {\n    fileIds = fileIds.filter(id => !e.message.includes(id)); // drop bad ids and retry\n  } else throw e;\n}","preventionTips":["Submit only ids returned by the files query for the current user.","Normalize ids to the same string format the validator uses.","Purge stale ids from draft state after uploads/deletions.","For group submissions, use group-scoped file listings."],"tags":["graphql","validation","attachments"],"backgroundTag":"invalid-argument-value","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"}