instructure/canvas-lms · error · StandardError
Invalid scope value. Valid options are: #
Error message
Invalid scope value. Valid options are: #{VALID_SCOPES.join(", ")}. What it means
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.
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
Example fix
// before rule.fix!(th, 'column') // after rule.fix!(th, 'col')
Defensive patterns
Strategy: validation
Validate before calling
const VALID_SCOPES = ['row', 'col', 'rowgroup', 'colgroup'];
if (!VALID_SCOPES.includes(value)) {
alert('Invalid scope value'); return;
}
rule.fix!(th, value) Type guard
const isValidScope = (v) => ['row','col','rowgroup','colgroup'].includes(v)
Try / catch
begin
rule.fix!(elem, value)
rescue StandardError => e
render json: { error: e.message, valid: ['row','col','rowgroup','colgroup'] }, status: :unprocessable_entity
end Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- [Accessibility::CategoryBreakdownService] dropping unknown…
- Caption cannot be empty.
- " " must be either " " or
- Improper status "# " for account # , skipping
- invalid context type
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/9e6c0ca55aac7752.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/accessibility/rules/table_header_scope_rule.rb:63
Accessibility::Forms::RadioInputGroupField.new(
label: I18n.t("Which part of the table does this heading apply to?"),
undo_text: I18n.t("Heading scope is now set up."),
value: options.first,
options:,
action: I18n.t("Set heading scope")
)
end
def fix!(elem, value)
scope_lookup_table = SCOPE_OPTIONS.to_h { |opt| [opt[:label].call, opt[:value]] }
scope = scope_lookup_table[value]
if scope
return { changed: nil } if elem["scope"] == scope
elem["scope"] = scope
else
raise StandardError, "Invalid scope value. Valid options are: #{VALID_SCOPES.join(", ")}." unless VALID_SCOPES.include?(value)
end
{ changed: elem, content_preview: table_preview(elem) }
end
def display_name
I18n.t("Table header set up incorrectly")
end
def message
I18n.t("This table headers isn't set up correctly for screen readers to know which cells it applies to.")
end
def why
I18n.t(
"This table header doesn't have scope set up. " \
"Scope tells screen readers which part of the table a heading applies to."
)View on GitHub (pinned to 1c9f0bb801)