instructure/canvas-lms · error · GraphQL::ExecutionError
filters must be an object
Error message
filters must be an object
What it means
UpdateWidgetDashboardConfig's validate_filters! requires the filters argument (a GraphQL JSON value) to be a Hash or ActionController::Parameters. Any other JSON type — array, string, number, null — raises this error before per-key validation.
Solutions
- Send filters as a JSON object whose keys are filter names
- Replace filters: [] with filters: {} to clear filters
- Coerce/validate the payload client-side before the mutation
- Ensure your HTTP layer preserves object shape (no accidental flattening)
Example fix
// before
updateWidgetDashboardConfig(input: {widgetId, filters: []})
// after
updateWidgetDashboardConfig(input: {widgetId, filters: {}}) // clear Defensive patterns
Strategy: type-guard
Validate before calling
const isFiltersObject = (f) => f !== null && typeof f === 'object' && !Array.isArray(f)
Type guard
const asFilters = (f) => (f !== null && typeof f === 'object' && !Array.isArray(f) ? f : {}) Try / catch
try { await gql(updateWidgetDashboardConfigMutation, {widgetId, filters}) } catch (e) { if (/filters must be an object/.test(e.message)) filters = {}; else throw e } Prevention
- Never send arrays or scalars as filters
- Use {} to clear filters
- Validate payload shape with a schema (zod/JSON Schema) before sending
When it happens
Trigger: Calling updateWidgetDashboardConfig with filters set to a JSON array, scalar, or null instead of an object like {"courseIds": [1,2]}.
Common situations: Client sends filters: [] (an empty array) as a 'clear' shortcut; JSON serialization turns an object into a scalar; API consumer misreads schema and sends a list of filters.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- A maximum of 50 assessees can be provided at once
- A maximum of 50 assessors can be provided at once
- All ConversationMessages must exist within the same…
- Anonymous assignments must be manually posted
- Assignment overrides are not allowed in the parent…
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/1505cdeefa314d7c.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/update_widget_dashboard_config.rb:74
VALID_ANNOUNCEMENT_FILTERS = %w[unread read all].freeze
VALID_TODO_FILTERS = %w[incomplete_items complete_items all].freeze
VALID_DATE_FILTERS = %w[not_submitted missing submitted].freeze
private
# TODO: Remove this entire helper method once platform-ui passes dashboard_type (EGG-2539)
# The resolver should directly use input[:dashboard_type] after that.
def educator_dashboard?(input)
return input[:dashboard_type] == "educator" if input[:dashboard_type].present?
context[:domain_root_account]&.feature_enabled?(:educator_dashboard) &&
current_user.educator_dashboard_user?
end
def validate_filters!(widget_id, filters)
# Accept Hash or ActionController::Parameters (which GraphQL JSON type may produce)
unless filters.is_a?(Hash) || filters.is_a?(ActionController::Parameters)
raise GraphQL::ExecutionError, "filters must be an object"
end
filters.each_key do |key|
unless key.is_a?(String) && !key.empty?
raise GraphQL::ExecutionError, "filter keys must be non-empty strings"
end
end
validate_filter_structure!(widget_id, filters)
end
def validate_filter_structure!(widget_id, filters)
if widget_id == ANNOUNCEMENTS_WIDGET_ID
validate_announcements_filters!(filters)
elsif widget_id == TODO_LIST_WIDGET_ID
validate_todo_list_filters!(filters)
elsif COURSE_WORK_WIDGET_IDS.include?(widget_id)
validate_course_work_filters!(filters)View on GitHub (pinned to 1c9f0bb801)