{"record":{"id":"025da3fb6d6e352a","repo":"instructure/canvas-lms","slug":"all-elements-must-be-user-objects","errorCode":null,"errorMessage":"all elements must be User objects","messagePattern":"all elements must be User objects","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"app/services/page_views/enqueue_batch_query_service.rb","lineNumber":62,"sourceCode":"\n      CanvasHttp.post(\n        uri.to_s,\n        request_headers,\n        content_type: \"application/json\",\n        body: request.to_json\n      ) do |response|\n        handle_generic_errors(response) unless response.code.to_i == 201\n        return response.header[\"Location\"].split(\"/\").last\n      end\n    end\n\n    private\n\n    def validate_users!(users)\n      raise ArgumentError, \"users must be an array\" unless users.is_a?(Array)\n      raise ArgumentError, \"users cannot be empty\" if users.empty?\n\n      raise ArgumentError, \"all elements must be User objects\" unless users.all?(User)\n    end\n\n    def parse_date_only(date_string)\n      raise ArgumentError, \"Date must be in YYYY-MM-DD format\" unless date_string.match?(/\\A\\d{4}-\\d{2}-\\d{2}\\z/)\n\n      Date.parse(date_string)\n    end\n\n    def collect_root_account_uuids(users)\n      users.flat_map do |user|\n        user.shard.activate do\n          user.root_account_ids.map do |id|\n            Account.find_cached(id).uuid\n          end\n        end\n      end.uniq\n    end\n  end","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/page_views/enqueue_batch_query_service.rb#L44-L80","documentation":"PageViews::EnqueueBatchQueryService#validate_users! raises ArgumentError when any element of the users array is not a User instance. The service uses each element's global_id and shard associations, so plain ids, hashes, or other records are rejected. Duck typing is not accepted; the check is users.all?(User).","triggerScenarios":"Calling call(start_date, end_date, [1, 2, 3]) with integer/global ids, [user.id], [user.attributes], or an array containing a mix of User objects and nils.","commonSituations":"Passing user ids collected from a form or API payload instead of loaded records; loading via select/pluck which returns raw values; forgetting nil entries after a compact-less filter.","solutions":["Load records first: User.where(id: ids).to_a","Compact the array to remove nils before calling","Map ids to User objects and verify all loaded (check count vs ids.uniq.count)"],"exampleFix":"// before\nPageViews::EnqueueBatchQueryService.call(start, end, user_ids, format)\n// after\nusers = User.where(id: user_ids).to_a\nPageViews::EnqueueBatchQueryService.call(start, end, users, format)","handlingStrategy":"type-guard","validationCode":"users = User.where(id: user_ids).to_a\nraise ArgumentError, 'not all users loaded' unless users.size == user_ids.uniq.size","typeGuard":"def all_users?(value)\n  value.is_a?(Array) && value.all?(User)\nend","tryCatchPattern":"begin\n  service.call(start_date, end_date, users, format)\nrescue ArgumentError => e\n  Rails.logger.error(\"invalid users argument: #{e.message}\")\nend","preventionTips":["Never pass pluck/select results — load full User records","Convert ids to records with User.where(id: ids).to_a","Verify loaded count matches requested ids","Compact nils from find_by-style lookups"],"tags":["ruby","type-mismatch","argument-validation"],"backgroundTag":"type-mismatch","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"}