{"record":{"id":"0ada1ddb5ea976d8","repo":"instructure/canvas-lms","slug":"only-one-assessor-is-allowed-when-creating-reciprocal-rules","errorCode":null,"errorMessage":"Only one assessor is allowed when creating reciprocal rules","messagePattern":"Only one assessor is allowed when creating reciprocal rules","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/allocation_rule_base.rb","lineNumber":86,"sourceCode":"    field :allocation_rules, [Types::AllocationRuleType], null: true\n\n    protected\n\n    def validate_feature_flag!(course)\n      unless course.feature_enabled?(:peer_review_allocation_and_grading)\n        raise GraphQL::ExecutionError, I18n.t(\"peer_review_allocation_and_grading feature flag is not enabled for this course\")\n      end\n    end\n\n    def validate_id_arrays!(input)\n      assessor_ids = input[:assessor_ids]\n      assessee_ids = input[:assessee_ids]\n      applies_to_assessor = input[:applies_to_assessor]\n      reciprocal = input[:reciprocal]\n\n      if reciprocal\n        if assessor_ids.length > 1\n          raise GraphQL::ExecutionError, I18n.t(\"Only one assessor is allowed when creating reciprocal rules\")\n        end\n        if assessee_ids.length > 1\n          raise GraphQL::ExecutionError, I18n.t(\"Only one assessee is allowed when creating reciprocal rules\")\n        end\n      elsif applies_to_assessor\n        if assessor_ids.length > 1\n          raise GraphQL::ExecutionError, I18n.t(\"Only one assessor is allowed when rule applies to assessor\")\n        end\n      elsif assessee_ids.length > 1\n        raise GraphQL::ExecutionError, I18n.t(\"Only one assessee is allowed when rule applies to assessee\")\n      end\n\n      if assessor_ids.empty?\n        raise GraphQL::ExecutionError, I18n.t(\"At least one assessor is required\")\n      end\n      if assessee_ids.empty?\n        raise GraphQL::ExecutionError, I18n.t(\"At least one assessee is required\")\n      end","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/allocation_rule_base.rb#L68-L104","documentation":"Raised by validate_id_arrays! in AllocationRuleBase when the mutation input has reciprocal: true but the assessor_ids array contains more than one ID. Reciprocal allocation rules pair exactly one assessor with exactly one assessee, so multiple assessors are rejected. The error surfaces as a GraphQL ExecutionError on the mutation result.","triggerScenarios":"Calling an allocation-rule create mutation with input { reciprocal: true, assessorIds: [id1, id2, ...], assesseeIds: [x] } — i.e. reciprocal flag set with assessor_ids.length > 1.","commonSituations":"A frontend batch-creates reciprocal rules in one call instead of one call per pair; copying a bulk-assessor payload from a non-reciprocal flow and adding reciprocal: true; misunderstanding that reciprocal means pair-wise and must be created one pair at a time.","solutions":["Send only one assessor ID when reciprocal: true.","Split multiple pairs into separate mutation calls (one per reciprocal pair).","Omit reciprocal: true if multiple assessors are actually intended.","Add client-side validation limiting assessorIds to length 1 when the reciprocal checkbox is on."],"exampleFix":"// before\n{ reciprocal: true, assessorIds: [\"11\", \"12\"], assesseeIds: [\"21\"] }\n// after (one pair per call)\n{ reciprocal: true, assessorIds: [\"11\"], assesseeIds: [\"21\"] }\n{ reciprocal: true, assessorIds: [\"12\"], assesseeIds: [\"21\"] }","handlingStrategy":"validation","validationCode":"if (input.reciprocal && input.assessorIds.length > 1) {\n  throw new Error('Reciprocal rules require exactly one assessor');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await createAllocationRules(input);\n} catch (e) {\n  if (e.message.includes('Only one assessor is allowed when creating reciprocal rules')) {\n    // fall back to per-pair mutation calls\n  }\n}","preventionTips":["Cap assessor selection to 1 in the UI when reciprocal is checked","Create reciprocal rules one pair per mutation call","Do not reuse bulk payloads across rule modes","Unit-test input builders per rule mode"],"tags":["graphql","validation","reciprocal-rules","canvas"],"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"}