Limiting responses per option in Google Forms

Google Forms has no native way to cap a choice. Here is what a working Apps Script has to handle, and the four ways a hand-written one fails without telling you.

Free generator — fills in your optionsNo signup. It hands you the script below with your own question and limits.

The one that actually breaks things

The obvious implementation counts picks per option, filters out the full ones, and writes the rest back with setChoiceValues(). That is correct right up until the array is empty.

A Google Forms choice question cannot have zero options. When the last seat of the last option goes, setChoiceValues([]) throws — and it throws inside an onFormSubmit trigger, where nobody sees it. The form carries on accepting bookings for slots that are already full, and you find out from the people who turn up.

The fix is to relabel instead of emptying:

getItem_(form).setChoiceValues(
  remaining.length ? remaining : original.map(function (o) { return o + ' — FULL'; })
);

Three more, in the order I hit them

Snapshot the original option list once, and only once

The script has to remember what the choices were before anything was removed. If you re-run setup after a slot has already closed, it snapshots the trimmed list — and that option is gone permanently, with nothing left to restore from. This is the one that loses data, and people hit it because re-running setup after editing the limits is the natural thing to do.

if (!props.getProperty('FCL_ORIGINAL')) {
  props.setProperty('FCL_ORIGINAL', JSON.stringify(current));
}

Use LockService

Two people submitting in the same second both read “one seat left” and both get it. A script lock around the recount is the entire fix, and without it the bug only appears on the day the form is busy — which is the day it matters.

var lock = LockService.getScriptLock();
try { lock.waitLock(30000); } catch (e) { return; }
try { /* recount and write back */ } finally { lock.releaseLock(); }

Match the question by exact title

Trailing whitespace in a question title is the single most common reason one of these scripts does nothing at all and reports no error. Throw loudly if the question is not found, rather than returning quietly.

Why not just use an add-on

You can, and for many people that is the right answer. The reason this exists is that a marketplace add-on needs access to your form and every response in it. For a school, a clinic or an HR team that is usually where the conversation stops. A script pasted into your own form’s Apps Script editor runs entirely inside your account: no third party, no marketplace, nothing to get reviewed.

The logic is open and tested

The exhaustion case above is covered by a test that runs against the shipped script, not against a copy of it — and the test was checked by removing the fix and confirming it fails. A test that cannot fail is not a test.

The free generator fills in your question title and limits and hands back the whole script. There is a $9 version on the same page for several questions at once, checkboxes and dropdowns, strike-through instead of removal, and auto-closing the form when everything is full.