jackwener/OpenCLI · error · CommandExecutionError

Mercury reimbursement form fields were not all filled: ${mis

Error message

Mercury reimbursement form fields were not all filled: ${missing.join(', ')}

What it means

Thrown when `fillReimbursementFields` reports that one or more of the six required expense fields (amount, currency, date, merchant, category, notes) were not marked as touched — i.e. the command could not confirm it actually set every field in the Mercury reimbursement form. The message lists the missing field keys. This prevents advancing to Review with a partially filled draft.

Source

Thrown at clis/mercury/reimbursement-draft.js:81

        if (!upload || upload.uploaded !== true || upload.files !== 1) {
            throw new CommandExecutionError('Mercury receipt upload did not confirm exactly one uploaded file');
        }
        if (upload.target !== RECEIPT_INPUT_SELECTOR || upload.matches_n !== 1) {
            throw new CommandExecutionError('Mercury receipt upload did not confirm the intended receipt input');
        }
        const uploadedNames = Array.isArray(upload.file_names) ? upload.file_names : [];
        if (!uploadedNames.includes(receiptBasename(input.receipt))) {
            throw new CommandExecutionError('Mercury receipt upload did not confirm the expected receipt file');
        }

        if (input.ocrWaitSeconds > 0) await page.wait({ time: input.ocrWaitSeconds });

        const fields = await fillReimbursementFields(page, input);
        await page.wait({ time: 1 });
        const expectedFields = ['amount', 'currency', 'date', 'merchant', 'category', 'notes'];
        const missing = expectedFields.filter((key) => !fields.touched[key]);
        if (missing.length > 0) {
            throw new CommandExecutionError(`Mercury reimbursement form fields were not all filled: ${missing.join(', ')}`);
        }

        const reviewClick = await page.evaluate(`(() => {
            const norm = (s) => String(s || '').replace(/\\s+/g, ' ').trim().toLowerCase();
            const candidates = Array.from(document.querySelectorAll('button, a, [role="button"], [role="link"]'))
              .filter((node) => {
                const style = window.getComputedStyle(node);
                return style.visibility !== 'hidden' && style.display !== 'none' && node.offsetParent !== null;
              });
            const el = candidates.find((node) => norm(node.innerText || node.textContent || '') === 'review');
            if (!el) return { clicked: false };
            el.click();
            return { clicked: true, text: el.innerText || el.textContent || '' };
        })()`);
        if (!reviewClick || typeof reviewClick !== 'object' || typeof reviewClick.clicked !== 'boolean') {
            throw new CommandExecutionError('Mercury returned malformed Review click result');
        }
        if (!reviewClick.clicked) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Increase --ocr-wait-seconds (default 8) so Mercury's OCR finishes before/after field filling doesn't overwrite values.
  2. Inspect the listed missing field in the Mercury dialog DOM and update its selector/logic in fillReimbursementFields (clis/mercury/utils.js).
  3. Verify the CLI argument values are non-empty and valid (date as YYYY-MM-DD, amount numeric, category matching a Mercury option).
  4. Check fillReimbursementFields still records fields.touched[key] = true for every successfully filled field after any refactor.

Example fix

// before
opencli mercury reimbursement-draft --receipt r.png --amount 140.00 --date 2026-06-26 --merchant "M" --notes "n"
// after (raise OCR wait and supply explicit currency/category)
opencli mercury reimbursement-draft --receipt r.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant "M" --category "Marketing & Advertising" --notes "n" --ocr-wait-seconds 15
Defensive patterns

Strategy: validation

Validate before calling

const required = ['receipt','amount','currency','date','merchant','category','notes'];
for (const k of required) {
  if (!kwargs[k] || String(kwargs[k]).trim() === '') {
    throw new Error(`missing required arg: ${k}`);
  }
}
if (!/^\d{4}-\d{2}-\d{2}$/.test(kwargs.date)) {
  throw new Error('date must be YYYY-MM-DD');
}

Type guard

function allFieldsTouched(fields, keys = ['amount','currency','date','merchant','category','notes']) {
  return keys.every((k) => fields?.touched?.[k] === true);
}

Try / catch

try {
  await opencli(['mercury', 'reimbursement-draft', ...args]);
} catch (e) {
  const m = String(e.message).match(/not all filled: (.+)/);
  if (m) {
    // retry with higher --ocr-wait-seconds for fields: m[1]
  } else throw e;
}

Prevention

When it happens

Trigger: Running reimbursement-draft when a field's selector no longer matches after a Mercury UI change, OCR of the uploaded receipt overwrites fields after fill (race with input.ocrWaitSeconds too short), a field is disabled/hidden (e.g. currency locked to a default), or input coercion produces an empty value that the fill helper skips.

Common situations: ocr-wait-seconds too low so Mercury's OCR pass rewrites amount/merchant/notes after the CLI fills them; category string not matching any Mercury option so the select never updates; date format rejected by the custom date picker; field keys missing from fillReimbursementFields' touched tracking after a utils.js refactor.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/c5148d59e17fc404. Report an issue: GitHub.