{"record":{"id":"26e0abdd2022da51","repo":"instructure/canvas-lms","slug":"invalid-file-type","errorCode":null,"errorMessage":"Invalid file type","messagePattern":"Invalid file type","errorType":"exception","errorClass":"SubmissionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/create_submission_draft.rb","lineNumber":162,"sourceCode":"  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?\n\n    joined_file_ids = file_ids.to_a.join(\",\")\n    sql = <<~SQL.squish\n      SELECT\n        p.a_id,\n        p.ra_id\n      FROM\n        (\n          (\n            SELECT\n              a.id as a_id,","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/create_submission_draft.rb#L144-L180","documentation":"CreateSubmissionDraft#verify_allowed_extensions! enforces the assignment's allowed_extensions list for online_upload submissions. If the assignment restricts extensions and any attachment's extension is not in the list (case-insensitive), it raises SubmissionError \"Invalid file type\".","triggerScenarios":"Drafting a file upload submission where assignment.allowed_extensions is non-blank and an attachment's after_extension (downcased) is not included, e.g. uploading .exe when only .pdf,.docx allowed; attachment with no extension while restrictions exist.","commonSituations":"Instructor tightened allowed extensions after students already uploaded files; file renamed without actually converting format; macOS hidden extension issues; uploading files with uppercase extensions is fine but files with none are rejected.","solutions":["Convert/rename the file so its extension matches one in the assignment's allowed_extensions.","Check the assignment's allowedExtensions via GraphQL and validate client-side before upload.","Ask the instructor to update allowed_extensions if the file type should be permitted.","Ensure the uploaded Attachment actually retains its extension (after_extension not empty)."],"exampleFix":"// before\nfileIds: [idOf(\"report.exe\")]\n// after\nfileIds: [idOf(\"report.pdf\")] // extension now in allowedExtensions: [\"pdf\"]","handlingStrategy":"validation","validationCode":"const allowed = (assignment.allowedExtensions || []).map(e => e.toLowerCase())\nconst bad = files.filter(f => !allowed.includes(f.name.split('.').pop().toLowerCase()))\nif (assignment.allowedExtensions?.length && bad.length) throw new SkipError('invalid file type: ' + bad.map(f => f.name))","typeGuard":"function hasAllowedExtension(file, allowed) { return !allowed?.length || allowed.includes((file.name.split('.').pop() || '').toLowerCase()); }","tryCatchPattern":"try {\n  await createSubmissionDraft({ fileIds })\n} catch (e) {\n  if (e.message === 'Invalid file type') {\n    alertUserToConvertFile(assignment.allowedExtensions)\n  } else throw e;\n}","preventionTips":["Read allowedExtensions from the assignment and filter in the file picker (accept attribute).","Convert files to a permitted format before upload rather than renaming.","Watch for files with no extension when restrictions exist."],"tags":["graphql","validation","file-upload"],"backgroundTag":"schema-validation-failed","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"}