Problem report — Plasmic : Inconsistent behavior when chaining Run actions code + Default submit (Use integration ) + Go to page with reCAPTCHA + Subbase
Short summary (1 sentence)
When chaining the form actions ( Run Code to validate reCAPTCHA via /api/ verify-captcha , Default submit that writes to Supabase and Go to (page to redirect to / thank-you ), the system executes the actions inconsistently: sometimes the submission to Supabase happens even without reCAPTCHA ; sometimes the submission is blocked but the redirection still occurs; in other combinations, the submission does not happen and yet there is still a redirection. The effect does not follow a clear rule, and there is no way to condition the Go to page to success of Default submit .
Environment / Integrations
-
Frontend built in Plasmic (visual editor). The form action “ on The ` submit` function has 3 possible steps in the following order:
-
Run code — JS code that performs a fetch ( "/api/ verify-captcha ", { method : “POST”, body: JSON.stringify ({ token : $ state.captcha _token. value } ) }) and returns / alert as a result.
-
Default submit — Action = Use integration → Supabase integration → Operation = Create row in the Corporate Waitlist table .
-
Go to page — Destination: / thank you
-
-
Backend reCAPTCHA endpoint /api/ verify-captcha ( Vercel / Next.js API), code shown in the repo (see print).
-
Supabase : integration configured in Plasmic as “New waitlist” "supabase ".
-
Variables and state in Plasmic : $ state.captcha_token , $ state.form2.value , $ state.form2.isSubmitting , etc.
-
Tests were performed in the published environment and in the Plasmic preview (behavior observations in both).
Relevant code used in the tests
/api/verify-captcha.js ( server side )
export default async function handler ( req , res) {
if ( req.method != = “POST”) return res.status (405 ). json ({ message : " Method not allowed " } );
const { token } = req.body ;
if (!token ) return res.status (400 ). json ({ message : " Missing reCAPTCHA token " } );
const secretKey = process.env.RECAPTCHA _SECRET_KEY ;
const verifyUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${token}\`;
const response = await fetch ( verifyUrl , { method : "POST " } );
const data = await response.json ();
if ( ! data.success ) return res.status (400 ). json ({ message : " reCAPTCHA verification failed " } );
return res.status (200 ). json ({ message : " reCAPTCHA successfully verified ", success : true } );
}
Run code (client) — validate reCAPTCHA :
const verify = await fetch ( "/api/ verify-captcha ", {
method : “POST”,
headers : { " Content-Type ": " application / json " } ,
body: JSON.stringify ({ token : $ state.captcha _token.value } ) ,
});
const data = await verify.json ();
if ( ! data.success ) {
alert ( " Please verify the reCAPTCHA before submitting .");
return ;
}
If it passed, let the flow continue (don’t send it to the subbase here).
(In a problematic test, there was code that also used await.) fetch ( “URL_DO_SUPABASE”, …) inside Run (code — this was removed because it conflicts with the Default submit .)
Steps to reproduce (minimal and straightforward)
-
In Plasmic , create a form with:
-
captcha_token field filled by the reCAPTCHA widget ,
-
ON SUBMIT action with three chained steps: Run code (check token), Default submit (Use integration → Supabase Create Row), Go to page → / thank-you .
-
-
On the website, do not check the reCAPTCHA and submit → observe the behavior.
-
Check the reCAPTCHA and submit → observe the behavior.
-
Try to change the order of the actions (e.g., without Run) code , just Default submit + Go to page ; or Run code + Go to (page without default submit ) and register results.
Observed behavior (detailed by combination) — actual test results
These four cases were repeated multiple times and show inconsistent patterns:
-
Run code ( validates) reCAPTCHA ) + Default submit
-
Observed result: if reCAPTCHA is not checked → Run The code is blocked (returns and does not proceed), and Default submit does NOT send to Supabase .
If reCAPTCHA is checked → Default submit sends correctly ( loading appears) and the data reaches Supabase . -
Status: OK — expected behavior.
-
-
Default submit + Go to page (without Run) code )
-
Observed result: even without reCAPTCHA (hidden captcha_token field empty), the Default submit The data was sent to Supabase , and then the screen was redirected to / thank-you .
-
Status: Unexpected — Expected: If you want to block using reCAPTCHA , without Run The code shouldn’t send; but the observation here is: without reCAPTCHA verification , the sending occurred. (This is just an observation—behavior that leads to the problem when we combine Run) Go to code page .)
-
-
Run code + Go to page (without Default submit )
-
Observed result: when reCAPTCHA is NOT checked → Run The code blocks it, but the Go to The page still runs and the user is taken to / thank-you .
-
Status: wrong — Go to This page should not execute if Run The code returned and canceled the submission.
-
-
Run Code + Default submit + Go to page (the order used in the flow)
-
Observed results (intermittent):
-
In some tests, with reCAPTCHA NOT checked → Run The code blocks (ok), however the page was still redirected to / thank-you (i.e., Go to (The page executed despite the block).
-
In other tests, with reCAPTCHA enabled → the Default submit It didn’t send , but there was still a redirection.
-
-
Status: erratic and inconsistent behavior. Any combination results in unpredictable outcomes.
-
Logs / evidence collected
-
Console network: XHR calls to /api/ verify-captcha appear when Run The code executes. JSON response { success : true } when the token is valid.
-
Visual observation: a loading icon appears when the Default submit sends to Supabase (event visible 100% when Supabase receives a row).
-
Sequence of events observed (samples):
-
Case A (it worked): Run code → verify ok → Default submit → loading → Supabase row created → then Go to page ( redirect ).
-
Case B (error): Run code → verify failed → Go to page ( redirect ) → Default submit not executed.
-
Case C (erratic): Default submit + Go to page → Supabase always receives (even without reCAPTCHA ).
-
-
Plasma UI messages : reCAPTCHA validation error shown in popup (in English). No visible logs of the " action" . " engine " on the Plasmic panel .
What we’ve already tried / changes tested
-
Remove Go to page and leave only Run code + Default submit → flow OK ( reCAPTCHA blocks or allows, and Default submit sends when allowed).
-
Remove Default submit and leave Run code + Go to page → Go to The page sometimes runs even when blocked.
-
Put delay setTimeout ( () => window.location .href = "/ thank-you ", 5000) in Run The `code → delay` method works, but it’s a poor workaround (it doesn’t guarantee that the submit completed).
-
Attempt to submit to Supabase via manual fetch in Run The code (duplicating the work) → worked technically, but conflicts with the standard flow / “ Default submit ” and is an inadequate solution.
-
Testing in a published environment vs. preview — similar behavior, with sporadic differences.
-
We checked /api/verify-captcha.js in the repo : the endpoint exists and responds correctly when a valid token is used.
-
We checked environment variables in Vercel (RECAPTCHA_SECRET_KEY) — present.
-
We observed that the Default submit does not have a visible way of displaying its “result” (success/failure) to the When event of another action .
Hypotheses (why this might be happening)
-
Immediate navigation breaks/ignores asynchronous promise
- Go to The page can force navigation before pending actions are completed, and this navigation cancels pending requests (or causes the Default submit to be executed improperly).
-
Ordering actions does not guarantee waiting for promises.
- Plasmic action engine possibly calls the actions sequentially, but does not wait. Promises returned by Run The code or Default submit . Or there is an inconsistency: Default submit may make a request outside the expected asynchronous flow.
-
Default submit is executed independently of Run. code when Go to page is present
- Perhaps Plasmic optimizes the flow in the presence of navigation (e.g., triggering the default submit in parallel) so as not to block UX, causing unpredictable behavior.
-
The “When” condition of Go to The page does not reference the status of previous actions.
- There is no (or it is not documented) token/status that represents “Default submit” . The " success " attribute should be used in the When field. Therefore, there is no way to condition the redirection on the successful completion of the submit .
-
Race problem internal condition
- The order and speed of asynchronous operations ( fetch verify , fetch supabase , redirection) creates race conditions that result in non-deterministic behavior .
-
Preview vs Publish
- Plasmic preview runs in a different environment, where integrations (allowed domain in Supabase , CORS, etc. ) influence the behavior. It has been observed that in some executions the preview does not send, but it does send on publish .
What we expect (desired/correct behavior)
-
Action Run Code that explicitly returns/resolves a boolean/ promise indicating success/failure.
-
Default submit should wait for the Run to resolve. code before executing (if Run code abort, Default submit does not execute).
-
Go to The page must wait for Default submit to complete successfully ( row creation confirmed) before navigating. It should be possible to condition the execution of Go to The page is based on the result ( success / failure ) of the default submit .
-
In summary: the actions on the On list Submit statements must be chained sequentially and synchronized with support for Promises / await .
Specific requests/questions to Plasmic support
-
Documentation : There is a supported mechanism for explicitly awaiting a Run. Is the code or the default submit before executing the next action ? How to return a Promise from Run? What code will the Plasma engine use to correctly await ?
-
To display the status of a Default submit : Is it possible to obtain a `lastAction.success` or `form2.hasSubmitted` / `form2.submissionResult` flag that indicates whether the Default submit was successful? How can this be referenced in the `When` field of the ` Go To` method? page ?
-
Go to behavior page : why Go to The page sometimes executes even when a Run command is in place . Did the previous code return (blocking)? Is there a known bug where navigation actions ignore the result of previous actions?
-
Sequencing / atomicity : how to ensure the sequence runs code → Default submit → Go to Should the page execute strictly in series and only proceed when each step has finished successfully?
-
Internal logs/ tracing : Can the team enable action execution logs when running in preview (or indicate how to extract an action log with timestamps ) so we can see the order and execution time of each action? This is critical for reproduction and correction.
-
Architectural recommendation : If the above flow is not supported, what is the best practice to:
-
validate reCAPTCHA server - side
-
Subscribe to the successful submission to Supabase .
-
Only then redirect the user,
without needing to manually duplicate calls to Supabase in Run . code ( avoid) race conditions , duplications)?
-
Evidence to attach to the ticket (I would attach these items)
-
Screenshot from the Plasmic editor showing the On list. submit with the 3 actions ( Run code / Default submit / Go to (page ). (already captured)
-
Code for /api/verify-captcha.js (snapshot from the repository ).
-
Code snippets from Run Code used to validate the reCAPTCHA (client).
-
Relationship between the tested combinations and outcomes (points 1–4 above).
-
Export of network timeline ( devtools capture showing calls to /api/ verify-captcha , calls to supabase , and redirection to / thank-you with timestamps ) — I’ve seen logs with calls to reCAPTCHA and page. loads ; attaching HAR would be ideal.
-
Mention of the state variables form2.isSubmitting , form2.value , captcha_token as available in the editor and screenshots showing where they appear.
Possible suggestions/workarounds (short and medium term)
-
Short workaround (what we’ve already tested):
- Remove Go to Page of the chain and perform the manual redirection at the end of the Run. code After explicitly waiting for the Default submit to finalize. However, since Default submit is a Plasma action , we don’t have a direct handle / promise . The alternative was to duplicate the submission to Supabase via fetch within the Run event . The code ( it works but it’s a duplicate).
-
Forced delay: use setTimeout ( window.location.href ="/ thank -you ", 3000) in a Run Code that runs at the end. It provides a delay but doesn’t guarantee that the submit completed. (You’ve used this before; it’s a workaround.)
-
Manually send to Supabase in Run code : perform the fetch to Supabase within Run code after verify-captcha . Then redirect. (Works; loses the visual integration of Plasmic and duplicates logic.)
-
Ask the Plasmic team : resource to return Promise from Run code and automatic await ; or an onSubmitSuccess event that can be used in the When event of Go to page .
Final questions / what do I need from Plasmic?
-
Plasmic stock engine awaits promises returned by Run Code ? If so, how should I write the Run code? code to ensure the system waits (e.g., return fetch (…). then (…) vs await )?
-
Is there documentation/guide for linking? What actions involve asynchronous calls and final navigation?
-
Can you check internally why Go to The page executes even in cases where Run Did the code return/abort? Are there internal flags that trigger Go to Independent page flow?
Conclusion and priority
This problem breaks the consistency guarantee for a critical flow ( reCAPTCHA validation → database insert → thank you page). For our platform, this is a blocking issue: we need to ensure that the client is only redirected after validation and database save. I request high priority and, if possible, a response with:
-
Exact steps to reproduce in the Plasmic environment .
-
If there is an incorrect configuration in our flow,
What is the official recommendation (or patch) for chaining/ awaiting? actions


