{"record":{"id":"9e6c0ca55aac7752","repo":"instructure/canvas-lms","slug":"invalid-scope-value-valid-options-are-valid-scopes-join","errorCode":null,"errorMessage":"Invalid scope value. Valid options are: #{VALID_SCOPES.join(\", \")}.","messagePattern":"Invalid scope value\\. Valid options are: #(.+?)\\.","errorType":"validation","errorClass":"StandardError","httpStatus":null,"severity":"error","filePath":"app/models/accessibility/rules/table_header_scope_rule.rb","lineNumber":63,"sourceCode":"\n        Accessibility::Forms::RadioInputGroupField.new(\n          label: I18n.t(\"Which part of the table does this heading apply to?\"),\n          undo_text: I18n.t(\"Heading scope is now set up.\"),\n          value: options.first,\n          options:,\n          action: I18n.t(\"Set heading scope\")\n        )\n      end\n\n      def fix!(elem, value)\n        scope_lookup_table = SCOPE_OPTIONS.to_h { |opt| [opt[:label].call, opt[:value]] }\n        scope = scope_lookup_table[value]\n        if scope\n          return { changed: nil } if elem[\"scope\"] == scope\n\n          elem[\"scope\"] = scope\n        else\n          raise StandardError, \"Invalid scope value. Valid options are: #{VALID_SCOPES.join(\", \")}.\" unless VALID_SCOPES.include?(value)\n        end\n\n        { changed: elem, content_preview: table_preview(elem) }\n      end\n\n      def display_name\n        I18n.t(\"Table header set up incorrectly\")\n      end\n\n      def message\n        I18n.t(\"This table headers isn't set up correctly for screen readers to know which cells it applies to.\")\n      end\n\n      def why\n        I18n.t(\n          \"This table header doesn't have scope set up. \" \\\n          \"Scope tells screen readers which part of the table a heading applies to.\"\n        )","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/accessibility/rules/table_header_scope_rule.rb#L45-L81","documentation":"TableHeaderScopeRule#fix! maps a user-supplied value through scope_lookup_table to set the scope attribute (row/col/rowgroup/colgroup) on a table header. If the value is not in VALID_SCOPES, it raises StandardError listing the valid options, because an invalid scope attribute is invalid HTML and breaks screen-reader table navigation.","triggerScenarios":"Calling fix! with a value not present in VALID_SCOPES (and not resolvable via scope_lookup_table), e.g. 'both', 'column', typo'd 'colum'; stale client sending an old enum value after the rule's options changed.","commonSituations":"Frontend option labels drift from backend expected values after localization changes; API consumers hard-coding scope strings; version mismatch between UI bundle and Rails code.","solutions":["Pass a value exactly matching one of VALID_SCOPES (row, col, rowgroup, colgroup)","Map the UI label back to a canonical scope value before calling fix!","Keep client-side option list in sync with the rule's VALID_SCOPES (derive from shared constant/i18n keys)","Rescue the error and re-render the form with the list of valid options"],"exampleFix":"// before\nrule.fix!(th, 'column')\n// after\nrule.fix!(th, 'col')","handlingStrategy":"validation","validationCode":"const VALID_SCOPES = ['row', 'col', 'rowgroup', 'colgroup'];\nif (!VALID_SCOPES.includes(value)) {\n  alert('Invalid scope value'); return;\n}\nrule.fix!(th, value)","typeGuard":"const isValidScope = (v) => ['row','col','rowgroup','colgroup'].includes(v)","tryCatchPattern":"begin\n  rule.fix!(elem, value)\nrescue StandardError => e\n  render json: { error: e.message, valid: ['row','col','rowgroup','colgroup'] }, status: :unprocessable_entity\nend","preventionTips":["Derive UI options from the same constant as the backend (shared API/i18n)","Use value/keys, not display labels, when submitting","Add contract tests covering all scope options"],"tags":["accessibility","validation","enum"],"backgroundTag":"invalid-enum-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"}