Forms SDK

Troubleshooting

Symptom to cause to check: the SDK form debugging runbook

Debugging an SDK form triangulates three views: the browser console (runtime warnings), validate_form_html (the static gate), and response read-back (what actually landed). When in doubt, read a response back.

First moves

  1. form.state() in the console: currentStepId, answers, responseId, finished. form.capabilities shows what is wired.

  2. Attach a firehose while reproducing:

    ["viewed", "started", "stepChanged", "stepCompleted", "completed", "disqualified", "saved", "error"]
      .forEach((e) => form.on(e, (p) => console.log("[surface]", e, p)));
  3. Re-run validate_form_html against the deployed HTML; drift since the last validation is the most common root cause after a record edit.

Symptom table

SymptomLikely causeCheck
init throws "could not fetch runtime config"Wrong or missing apiBaseUrlThe error names the URL it tried; use the one from get_sdk_binding_map
No responses at allpreview: true left in the deployed pageGrep the page for preview; saved never fires in preview
No responses at allForm never published, so IDs don't matchConsole shows unbound-question warnings; run publish_form
Some answers missingBinding drift: a question ID unbound or misspelledConsole names the unbound IDs; validate_form_html
Some answers missingPartial responses off and the visitor abandoned mid-formtrackPartialResponses in settings; expected
400 "Fields are missing or incorrectly formatted"Hand-built setAnswer with the wrong shapeCompare with the binding map's answerShape
Choice answers wrong or emptyvalue attributes drifted from the record's option keysvalidate_form_html warns on exactly this
Same email accepted one day, rejected the nextA fail-open verdict later resolving definitively; expectedCheck failed on the verdict
Thank-you never showsNo thank-you ending step, or no container for itA step with endStepKind: "thank_you" and its container
Disqualified visitors see the thank-youNo disqualified ending step on the recorddisqualify() only reveals a "disqualified" ending
Steps don't switchdata-step-id values don't match record step IDs, or invalid gotoConsole warns on invalid goToStep
Duplicate responses per visitorinit called again without destroy() (SPA remounts)One engine per page life
GTM, GA4, or Meta events not firingPreview mode, or settings not setDeployed non-preview page; window.dataLayer after a step submit
Ad conversions not firingRules gate on trigger and URL conditions; terminal rules fire on completion only; queued until first responseIdRules in the dashboard; watch for saved first
Enrichment fields emptyNo enabled enrichment providerform.capabilities.enrichment; debug: true warns at boot
Scheduler says "No availability"The event type has no bookable slotslist_schedulers with includeAvailability: true
Lead journey emptyJourney ID never carried (or journey: false)Save payload's meta for userJourneyId
Lead attribution missingIdentify blocked (preview, ad blocker), or stale SPA sessionform.identify(); network tab for the identify call
Slower than the hosted formA custom header or JSON content type added preflightsDevTools should show no OPTIONS requests
Finished response stored as partialPage torn down mid-flightawait form.submit(), then await form.flush() before programmatic close

Reading a response back

list_responses (newest first), then get_response. Confirm: every expected question, structured choice answers, surfaceTagStatus: "sdk", and, for a booking step, eventScheduled: true. A response that looks right here ends the client-side investigation.

When the record and the HTML disagree

get_sdk_binding_map is the arbiter: the source: "live" map is what the deployed SDK actually fetched; the draft map (default) is what the next publish makes true. A page validated against the draft but running against an older live record explains most "validator says fine, runtime disagrees" reports. Publish, or validate against source: "live".

Still stuck? The npm package ships a longer runbook at skills/surface-forms-sdk/debugging.md, and debug: true narrates everything the SDK does.

On this page