{"record":{"id":"d77a7e636f9de5f8","repo":"instructure/canvas-lms","slug":"invalid-filter-keys-for-todo-list-widget-invalid-keys-join","errorCode":null,"errorMessage":"invalid filter keys for todo list widget: #{invalid_keys.join(\", \")}","messagePattern":"invalid filter keys for todo list widget: #(.+?)","errorType":"validation","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/update_widget_dashboard_config.rb","lineNumber":122,"sourceCode":"    end\n\n    invalid_keys = filters.keys - [\"filter\"]\n    unless invalid_keys.empty?\n      raise GraphQL::ExecutionError, \"invalid filter keys for announcements widget: #{invalid_keys.join(\", \")}\"\n    end\n  end\n\n  def validate_todo_list_filters!(filters)\n    if filters.key?(\"filter\")\n      filter_value = filters[\"filter\"]\n      unless filter_value.is_a?(String) && VALID_TODO_FILTERS.include?(filter_value)\n        raise GraphQL::ExecutionError, \"filter must be one of: #{VALID_TODO_FILTERS.join(\", \")}\"\n      end\n    end\n\n    invalid_keys = filters.keys - [\"filter\"]\n    unless invalid_keys.empty?\n      raise GraphQL::ExecutionError, \"invalid filter keys for todo list widget: #{invalid_keys.join(\", \")}\"\n    end\n  end\n\n  def validate_course_work_filters!(filters)\n    if filters.key?(\"selectedCourse\")\n      course_value = filters[\"selectedCourse\"]\n      unless course_value.is_a?(String) && (course_value == \"all\" || course_value.match?(/^course_\\d+$/))\n        raise GraphQL::ExecutionError, \"selectedCourse must be 'all' or 'course_{id}'\"\n      end\n    end\n\n    if filters.key?(\"selectedDateFilter\")\n      date_filter = filters[\"selectedDateFilter\"]\n      unless date_filter.is_a?(String) && VALID_DATE_FILTERS.include?(date_filter)\n        raise GraphQL::ExecutionError, \"selectedDateFilter must be one of: #{VALID_DATE_FILTERS.join(\", \")}\"\n      end\n    end\n","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/update_widget_dashboard_config.rb#L104-L140","documentation":"The todo list widget filter hash only allows the key \"filter\". validate_todo_list_filters! raises this GraphQL::ExecutionError when any other key appears in the hash, keeping persisted widget config strictly whitelisted.","triggerScenarios":"Sending a mutation for the todo list widget with filters such as {\"filter\": \"all\", \"showCompleted\": true} or {\"status\": \"open\"}.","commonSituations":"Reusing filter payloads built for other widgets (announcements uses \"filter\" only too, but course work uses selectedCourse/selectedDateFilter); adding new UI toggles without extending the server whitelist; key typos.","solutions":["Send only {\"filter\": \"...\"} for the todo list widget","Move any other settings out of the filters hash or extend validate_todo_list_filters! if a new key is required","Double-check the widget_id matches the intended widget"],"exampleFix":"// before\nfilters: { \"filter\": \"all\", \"groupBy\": \"course\" }\n// after\nfilters: { \"filter\": \"all\" }","handlingStrategy":"validation","validationCode":"const invalid = Object.keys(filters).filter((k) => k !== \"filter\");\nif (invalid.length) throw new Error(`invalid filter keys for todo list widget: ${invalid.join(\", \")}`);","typeGuard":"const isTodoFilterShape = (f) => f && Object.keys(f).every((k) => k === \"filter\");","tryCatchPattern":null,"preventionTips":["Keep todo list settings other than \"filter\" outside the filters hash","Type the filters payload strictly (e.g. { filter: TodoFilter })","Review payloads in network logs when adding new UI options"],"tags":["graphql","validation","unknown-key"],"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"}