instructure/canvas-lms · error · SubmissionError

No attachments found for the following ids

Error message

No attachments found for the following ids: %{ids}

What it means

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.

Solutions

  1. Re-query the current user's available files and submit only ids returned there.
  2. Remove the offending ids named in the error message before retrying.
  3. Re-upload the deleted file and use the new attachment id.
  4. Ensure you pass legacy numeric ids (or the expected format) consistently, not relay/global ids the validator can't match.

Example fix

// before
fileIds: [123, 456] // 456 deleted
// after
fileIds: [123, 789] // re-upload and use fresh Attachment id
Defensive patterns

Strategy: validation

Validate before calling

function assertValidFileIds(submittedIds, allowedIds) {
  const bad = submittedIds.filter(id => !allowedIds.includes(String(id)));
  if (bad.length) throw new SkipError(`invalid file ids: ${bad.join(',')}`);
}

Type guard

function allOwned(attachments, userId) { return attachments.every(a => a?.uploaderId === userId || a?.ownerId === userId); }

Try / catch

try {
  await createSubmissionDraft({ fileIds })
} catch (e) {
  if (e.message.includes('No attachments found')) {
    fileIds = fileIds.filter(id => !e.message.includes(id)); // drop bad ids and retry
  } else throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/4e4e8075b242c870. Report an issue: GitHub.

Appendix: source

Thrown at app/graphql/mutations/create_submission_draft.rb:151

      shard.activate do
        valid_attachment_ids = get_attachment_ids(attachments.map(&:id))
        validate_file_ids!(attachments, valid_attachment_ids)
        return_attachments += Attachment.active.where(id: valid_attachment_ids)
      end
    end

    return_attachments.each do |attachment|
      verify_authorized_action!(attachment, :read)
    end
    return_attachments
  end

  def validate_file_ids!(file_id_attachments, valid_attachment_ids)
    file_ids = file_id_attachments.pluck(:id).map(&:to_s)
    file_ids.each do |file_id|
      next if valid_attachment_ids.include?(file_id)

      raise SubmissionError, I18n.t(
        "No attachments found for the following ids: %{ids}",
        { ids: file_ids - valid_attachment_ids }
      )
    end
  end

  # TODO: move this into the model
  def verify_allowed_extensions!(assignment, attachments)
    return if assignment.allowed_extensions.blank?

    raise SubmissionError, I18n.t("Invalid file type") unless attachments.all? do |attachment|
      attachment_extension = attachment.after_extension || ""
      assignment.allowed_extensions.include?(attachment_extension.downcase)
    end
  end

  def get_attachment_ids(file_ids)
    return [] if file_ids.empty?

View on GitHub (pinned to 1c9f0bb801)