{"record":{"id":"6dbb0895b9be2086","repo":"docusealco/docuseal","slug":"message","errorCode":null,"errorMessage":"#{message}","messagePattern":"#\\{message\\}","errorType":"validation","errorClass":"Params::BaseValidator::InvalidParameterError","httpStatus":422,"severity":"error","filePath":"lib/params/base_validator.rb","lineNumber":40,"sourceCode":"\n    attr_reader :params, :dry_run\n\n    alias dry_run? dry_run\n\n    def initialize(params, dry_run: false)\n      @params = params\n      @dry_run = dry_run\n      @current_path = ''\n    end\n\n    def call\n      raise NotImplementedError\n    end\n\n    def raise_error(message)\n      message += \" in `#{@current_path}`.\" if @current_path.present?\n\n      raise InvalidParameterError, message unless dry_run?\n    end\n\n    def required(params, keys, message: nil)\n      keys = Array.wrap(keys)\n\n      return if keys.any? { |key| params&.dig(key).present? }\n\n      raise_error(message || \"#{keys.join(' or ')} is required\")\n    end\n\n    def type(params, key, type, message: nil)\n      return if params.blank?\n      return if params[key].blank?\n\n      return if params[key].is_a?(type) || (type == Hash && params[key].is_a?(ActionController::Parameters))\n\n      type = 'Object' if type == Hash\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/docusealco/docuseal/blob/004a22c1c88109c7ba0b567df011a8cb13894001/lib/params/base_validator.rb#L22-L58","documentation":"Params::BaseValidator is the base class for structured API parameter validation; raise_error appends the current JSON path ('in `path`') and raises InvalidParameterError unless the validator runs in dry_run mode. The literal message is interpolated - each concrete validator supplies texts like 'x or y is required' from the required/type helpers. These errors back the API's invalid-parameter (422-style) responses.","triggerScenarios":"Calling a validated API endpoint with missing required keys, wrong types, or values failing nested checks - for example a create call missing every key passed to required(), or a string sent where the type check expects an array.","commonSituations":"API consumers omitting optional-looking-but-required fields; wrong request nesting (the path suffix in the message shows where); schema changes between versions; integrations written against outdated docs.","solutions":["Read the message literally: it names the exact parameter and the JSON path where validation failed.","Diff your payload against the endpoint's documented schema and fix nesting per the path suffix.","Use the validator's dry_run mode in tests to collect all errors at once instead of one per request.","If the message seems wrong, check the endpoint's validator class for the exact required/type rules."],"exampleFix":"# before\nrequired(params, %i[template_id])\n\n# after -- clearer message for API consumers\nrequired(params, %i[template_id], message: 'template_id or template_blob is required')","handlingStrategy":"validation","validationCode":"// client-side: check required keys before sending\nconst REQUIRED = ['template_id']\nconst missing = REQUIRED.filter((k) => body[k] == null)\nif (missing.length) throw new Error(`Missing: ${missing.join(', ')}`)","typeGuard":"const hasRequiredKeys = (b) =>\n  typeof b === 'object' && b !== null && REQUIRED.every((k) => b[k] != null && b[k] !== '')","tryCatchPattern":"try {\n  await api.post('/api/submissions', body)\n} catch (e) {\n  if (e.status === 422) mapFieldErrors(e.body) // message names the param + path\n  else throw e\n}","preventionTips":["Generate typed clients from the API schema","Use dry-run validators in integration tests","Log the full rejected payload (sans secrets) on 422"],"tags":["rails","api","validation","invalid-parameter","dry-run"],"backgroundTag":"schema-validation-failed","analyzedSha":"004a22c1c88109c7ba0b567df011a8cb13894001","analyzedAt":"2026-08-21T13:38:23.343Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}