{"record":{"id":"5a8415377a624782","repo":"instructure/canvas-lms","slug":"override-parameter-must-be-a-hash","errorCode":null,"errorMessage":"override parameter must be a hash","messagePattern":"override parameter must be a hash","errorType":"validation","errorClass":"ArgumentError","httpStatus":400,"severity":"error","filePath":"app/controllers/microfrontends_release_tag_override_controller.rb","lineNumber":62,"sourceCode":"\n  def destroy\n    service = MicrofrontendsReleaseTagOverrideService.new(session)\n    service.clear_overrides\n\n    redirect_to request.referer || root_url\n  end\n\n  private\n\n  def validate_environment\n    not_found unless Setting.get(\"allow_microfrontend_release_tag_override\", \"false\") == \"true\"\n  end\n\n  def validate_params!\n    override_params = params[:override]\n\n    unless override_params.respond_to?(:each)\n      raise ArgumentError, \"override parameter must be a hash\"\n    end\n\n    override_params.each do |app, assets_url|\n      next if assets_url.blank?\n\n      unless SUPPORTED_APPS.include?(app)\n        raise ArgumentError, \"app '#{app}' must be one of: #{SUPPORTED_APPS.join(\", \")}\"\n      end\n\n      begin\n        uri = URI.parse(assets_url)\n        unless ALLOWED_HOSTS.include?(uri.host)\n          raise ArgumentError, \"assets_url host for '#{app}' must be one of: #{ALLOWED_HOSTS.join(\", \")}\"\n        end\n      rescue URI::InvalidURIError\n        raise ArgumentError, \"assets_url for '#{app}' must be a valid URL\"\n      end\n    end","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/microfrontends_release_tag_override_controller.rb#L44-L80","documentation":"Raised by MicrofrontendsReleaseTagOverrideController#validate_params! when the override parameter does not respond to #each — i.e. it is not a hash-like structure. The controller expects params[:override] to map app names to assets URLs, and rejects anything else before iterating.","triggerScenarios":"POST/PUT to the microfrontends release tag override endpoint with params[:override] missing, sent as a scalar/string, or serialized as a JSON array; sending override=null; double-encoding the payload so Rails parses it as a string.","commonSituations":"Scripts posting JSON without proper Content-Type so body arrives as a string; sending nested params as JSON body while the endpoint expects Rails-style form params; typos like overrides instead of override.","solutions":["Send override as a hash/object mapping app -> assets_url, e.g. { override: { app: 'https://...' } }","Ensure the request Content-Type (application/json or form encoding) matches the payload shape so Rails parses it as a hash","Fix any typo in the parameter name (override, not overrides)","Validate payload shape client-side before sending"],"exampleFix":"// before\n{ 'override': ['k5', 'https://cdn.example.com'] }\n// after\n{ 'override': { 'k5': 'https://cdn.example.com' } }","handlingStrategy":"validation","validationCode":"const override = payload.override;\nif (!override || typeof override !== 'object' || Array.isArray(override)) throw new Error('override must be an object');","typeGuard":"const isHash = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"begin\n  validate_params!\nrescue ArgumentError => e\n  render json: { error: e.message }, status: :bad_request\nend","preventionTips":["Send override as a JSON object with matching Content-Type","Check param name spelling (override)","Unit-test the controller with malformed payloads"],"tags":["ruby","params-validation"],"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"}