onetimesecret/onetimesecret · warning · ApplicationError

incoming.validation_form_errors

Error message

incoming.validation_form_errors

What it means

After the feature check, submit() runs validateForm(): memo is optional but must not exceed memoMaxLength (from config, default 50), secret must be non-empty after trim, and recipientId must be chosen. If any check fails, this aggregate human error is thrown while the per-field messages land in the errors ref (incoming.validation_secret_required, incoming.validation_recipient_required, incoming.validation_memo_too_long) for inline display.

Source

Thrown at src/shared/composables/useIncomingSecret.ts:151

   * Creates API payload from form data
   */
  const createPayload = (): IncomingSecretPayload => ({
    memo: form.value.memo.trim() || '', // Empty string if no memo provided
    secret: form.value.secret,
    recipient: form.value.recipientId,
  });

  /**
   * Handles form submission
   */
  const submit = () =>
    wrapSubmit(async () => {
      if (!isFeatureEnabled.value) {
        throw createError(t('incoming.validation_feature_disabled'), 'human');
      }

      if (!validateForm()) {
        throw createError(t('incoming.validation_form_errors'), 'human');
      }

      const payload = createPayload();
      const response = await incomingStore.createIncomingSecret(payload);

      if (options?.onSuccess) {
        await options.onSuccess(response);
      } else if (response.success && response.record?.receipt?.key) {
        // Default navigation to success view
        await router.push({
          name: 'IncomingSuccess',
          params: { receiptKey: response.record.receipt.key },
        });
      }

      return response;
    });

View on GitHub (pinned to f81295e41b)

Solutions

  1. Read the errors ref after the failure - it names the exact offending field(s).
  2. Disable the submit button while !isFormValid (the composable already exposes it) so this throw never fires.
  3. Bind :maxlength="memoMaxLength" on the memo input to make the length failure impossible.
  4. Require the recipient select before enabling submit.

Example fix

// before
<button @click="submit">Send</button>

// after
<button :disabled="!isFormValid || isSubmitting" @click="submit">Send</button>
<input v-model="form.memo" :maxlength="memoMaxLength" />
Defensive patterns

Strategy: validation

Validate before calling

const canSubmit = computed(
  () =>
    !!form.value.secret.trim() &&
    !!form.value.recipientId &&
    form.value.memo.length <= memoMaxLength.value
)
if (!canSubmit.value) {
  // highlight offending fields; do not call submit()
}

Try / catch

submit() toasts the aggregate message via wrap; rely on the errors ref for per-field display and isFormValid to prevent the call entirely. No try/catch is needed.

Prevention

When it happens

Trigger: Empty or whitespace-only secret; empty recipientId; memo longer than memo_max_length from /api/incoming/config (default 50 characters).

Common situations: User hits submit before selecting a recipient; long pasted memo because the input lacks a maxlength binding; programmatic submit without touching the form fields.

Related errors


AI-assisted analysis of onetimesecret/onetimesecret@f81295e41b (2026-08-23). Data as JSON: /api/errors/1a6867e0ec4186ac. Report an issue: GitHub.