docusealco/docuseal · error · Submissions::CreateFromSubmitters::BaseError

Invalid submitter params

Error message

Invalid submitter params

What it means

Submissions::CreateFromSubmitters raises BaseError 'Invalid submitter params' (lib/submissions/create_from_submitters.rb:59) when a submitter entry cannot be matched to any party defined by the template. The uuid is resolved by find_submitter_uuid from (in priority) attrs[:uuid], a case-insensitive role name match on attrs[:role], or positional index; if that resolved uuid does not exist in template_submitters, template_submitter stays nil and the error fires.

Source

Thrown at lib/submissions/create_from_submitters.rb:59

            uuid = template_submitter['uuid']
          else
            if submitter_attrs[:roles].present? && submitter_attrs[:roles].size == 1
              submitter_attrs[:role] = submitter_attrs[:roles].first
            end

            uuid = find_submitter_uuid(template_submitters, submitter_attrs, index)

            next if uuid.blank?
            next if submitter_attrs.slice('email', 'phone', 'name').compact_blank.blank?

            submission.template_fields = submission.template.fields if submitter_attrs[:completed].present? &&
                                                                       submission.template_fields.blank?

            template_submitter = template_submitters.find { |e| e['uuid'] == uuid }
          end

          raise BaseError, 'Invalid submitter params' unless template_submitter

          template_submitter = template_submitter.except('optional_invite_by_uuid', 'invite_by_uuid',
                                                         'invite_via_field_uuid')

          template_submitter['order'] = submitter_attrs['order'] if submitter_attrs['order'].present?

          submission.template_submitters << template_submitter

          is_order_sent = submitters_order == 'random' ||
                          (template_submitter['order'] || submitter_attrs[:index] || index).zero?

          build_submitter(submission:, attrs: submitter_attrs,
                          uuid:, is_order_sent:, user:, params:,
                          preferences: preferences.merge(submission_preferences))
        end

        maybe_set_dynamic_documents(submission)
        maybe_set_template_fields(submission, attrs[:submitters], with_template:, new_fields:)

View on GitHub (pinned to 004a22c1c8)

Solutions

  1. Fetch the template's parties first (template.submitters names/uuids) and send only role (case-insensitive exact), uuid, or in-range index values that match them.
  2. If you need one signer to cover several roles, send roles: ['First Role', 'Second Role'] on a single entry instead of duplicating entries with made-up roles.
  3. Remove surplus submitter entries — every entry must map to a template party (entries without email/phone/name are skipped by design at line 51).
  4. Regenerate any hard-coded uuids after a template is duplicated or edited.

Example fix

# before
Submissions::CreateFromSubmitters.call(
  template:, user:, source: 'api', submitters_order: 'preserved',
  submissions_attrs: [{ submitters: [{ role: 'Singger', email: 'a@b.c' }] }] # typo
)

# after
Submissions::CreateFromSubmitters.call(
  template:, user:, source: 'api', submitters_order: 'preserved',
  submissions_attrs: [{ submitters: [{ role: template.submitters.first['name'], email: 'a@b.c' }] }]
)
Defensive patterns

Strategy: validation

Validate before calling

# Validate submitters attrs against the template before calling CreateFromSubmitters
def valid_submitter_params?(template, submitters_attrs)
  known_uuids = template.submitters.map { |s| s['uuid'] }
  known_roles = template.submitters.map { |s| s['name'].downcase }

  Array(submitters_attrs).all? do |attrs_entry|
    Array(attrs_entry[:submitters]).all? do |s|
      next true if s[:uuid].present? && s[:uuid].in?(known_uuids)
      next true if s[:role].present? && known_roles.include?(s[:role].to_s.downcase)
      next true if s[:uuid].blank? && s[:role].blank? && # index fallback in range
                   (s[:index] || submitters_attrs.index(attrs_entry)).to_i < known_uuids.size

      false
    end
  end
end

Try / catch

begin
  Submissions::CreateFromSubmitters.call(template:, user:, submissions_attrs:, source:, submitters_order:)
rescue Submissions::CreateFromSubmitters::BaseError => e
  render json: { error: e.message }, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: POST /api/submissions with a submitters entry whose role/uuid matches none of template.submitters (typo'd role name like 'Signer ' vs 'Signer'); an entry with no uuid/role falling back to an index beyond the template's party count; a uuid copied from a different template; the merge path (attrs[:roles]) raising earlier but leaving a nil template_submitter from a previous skipped iteration.

Common situations: Template roles renamed after an integration was coded; templates recreated and old cached uuids reused; clients sending more subitters than roles expecting extras to be ignored; API consumers assuming 1-based or uuid-based ordering that does not match find_submitter_uuid's index fallback.

Related errors


AI-assisted analysis of docusealco/docuseal@004a22c1c8 (2026-08-21). Data as JSON: /api/errors/a33b41496e7a130c. Report an issue: GitHub.