Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

The error is usually caused by your form markup, not Cypress. A descendant control with name="submit" or id="submit" masks the form’s native submit() method. Rename or remove that attribute, then use form.requestSubmit() when you want normal browser validation and submit-event handling. Cypress can expose the problem when a test presses Enter in a form field and the application’s submission code runs.

What the TypeError actually means

HTMLFormElement normally provides a callable submit() method. However, form controls are also exposed as properties on their owner form. If an input, button, or another descendant has the name or ID submit, that control can replace the method when code evaluates form.submit. Calling the resulting element as a function produces an error such as TypeError: form.submit is not a function.

MDN describes this directly: a form control with a name or id of submit masks the form’s submit method. The page was last modified April 10, 2025.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A minimal collision

<form id="profile-form">
  <input name="email" type="email">
  <button id="submit" type="submit">Save</button>
</form>

<script>
  const form = document.querySelector('#profile-form');
  form.submit(); // TypeError: form.submit is not a function
</script>

In this example, form.submit resolves to the button identified as submit, not to the native function. The same issue occurs with <input name="submit">, even when the control has no matching ID.

Find the control that is shadowing submit

Start with the stack trace. The failing line tells you whether your application, a form helper, or a test utility is calling submit(). Then inspect the actual form rendered in the browser; do not rely only on a template or component source file.

  1. Open the failing Cypress test and identify the form selector involved.
  2. In the Cypress runner or browser DevTools, inspect that form and every descendant input, button, select, and other form-associated control.
  3. Search the rendered HTML and component templates for name="submit" and id="submit".
  4. Check dynamically generated controls as well as static markup. A component library can add an ID at runtime.
  5. Confirm the property value in the console. For a healthy form, typeof form.submit is "function"; with a collision it is commonly "object" (an element).
const form = document.querySelector('#profile-form');
console.log(form.submit);
console.log(typeof form.submit);
console.log([...form.elements].map(control => ({
  tag: control.tagName,
  id: control.id,
  name: control.name
}))); 

The important detail is the rendered form. Cypress does not create the conflicting name or ID. It merely drives the page far enough for your application code to encounter it.

Fix the markup by renaming or removing the collision

Use a purpose-specific ID or name for the submit button. If the server does not need a value from the button, omit its name entirely.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<form id="profile-form">
  <input name="email" type="email" required>
  <button id="save-profile" type="submit">Save</button>
</form>

Names and IDs often have different jobs. Keep a backend field name when it is required, but choose an ID that cannot collide with a form method, such as save-profile. Update Cypress selectors at the same time so the test describes the control’s purpose rather than the old conflicting ID.

cy.get('#save-profile').click();
// or, for the field-to-submit path:
cy.get('input[name="email"]').type('{enter}');

After changing the component, rerun the test from a clean page load. If a framework keeps an old component instance mounted, a stale DOM node can make it appear that the rename did not work.

Choose requestSubmit() or submit() deliberately

Renaming the control restores the native method, but you still need the right submission API. The two methods have intentionally different behavior.

API Constraint validation submit event and handlers Submitter button Use when
form.submit() Skipped Not dispatched Not represented You explicitly need a low-level submission that bypasses validation and event handlers.
form.requestSubmit() Runs Dispatched when validation succeeds Optional; pass a specific button You want behavior equivalent to activating a submit button.

For normal application behavior, prefer requestSubmit(). It runs browser constraint validation and dispatches the form’s submit event, allowing the application’s listener to run.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const form = document.querySelector('#profile-form');
form.requestSubmit();

When a particular button controls the operation, pass that button explicitly:

const form = document.querySelector('#profile-form');
const saveButton = form.querySelector('#save-profile');
form.requestSubmit(saveButton);

The submitter must belong to the form. Passing a button from another form, or a non-submit control, is an error. A submitter also matters when buttons have different name/value pairs or per-button attributes such as a different target or encoding.

Do not treat form.submit.click() as a general repair. After a collision, that property may be an element, but code that depends on the collision is fragile. Identify the intended button and call requestSubmit(button), or rename the control and use the form API.

How Cypress reaches the failing code path

Cypress documents implicit form submission when a test types {enter} into an input associated with a form. Depending on the structure, the browser-like interaction submits the form and Cypress can synthesize a click on its submit button. Your application’s submit listener then runs and may call the shadowed form.submit().

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
it('saves the profile when Enter is pressed', () => {
  cy.visit('/profile');
  cy.get('#profile-form input[name="email"]')
    .clear()
    .type('[email protected]{enter}');
});

If the listener calls event.preventDefault(), the submit event can still be observed while the browser’s navigation or network submission is prevented. That is normal for single-page applications. The TypeError is separate: it occurs when the listener or another function tries to call a property that has been replaced by a control.

To make the test’s intent explicit, click the corrected submit button:

cy.get('#save-profile').click();

Use the Enter test as well when keyboard submission is a supported requirement. Fixing the ID does not remove the need to test both interaction paths if your UI promises both.

A Cypress-focused debugging sequence

  1. Capture the complete stack trace. Find the first application frame that evaluates form.submit(); Cypress command frames are usually only the route that triggered it.
  2. Log the selected element. Use cy.get('#profile-form').then($form => { ... }) and inspect $form[0].submit, its type, and the form’s elements.
  3. Search both attributes. Look for name="submit" and id="submit"; either one can cause the collision.
  4. Rename the control. Change the markup and any selectors, labels, accessibility references, or component props that depended on the old ID.
  5. Replace programmatic calls where appropriate. Use requestSubmit() when validation and submit handlers are part of the intended behavior.
  6. Rerun the original interaction. Test the exact Enter or click path that exposed the problem, not only a direct call from a test helper.
cy.get('#profile-form').then($form => {
  const form = $form[0];
  cy.log(`submit property type: ${typeof form.submit}`);
  expect(typeof form.submit).to.equal('function');
});

Common symptoms and fixes

The button has no name, but the error remains

Check its id. The collision is caused by either attribute, and an ID is enough. Also inspect hidden controls and controls inserted by a component after the initial render.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The form was renamed, but Cypress still reports the error

Inspect the live DOM in the Cypress browser. You may be visiting a different route, rendering an older bundle, or mounting another form with a control still named or identified as submit. Search the entire rendered document, not only the visible section.

The TypeError is gone, but the submit handler does not run

That is expected if the code still uses direct form.submit(). Direct submission bypasses constraint validation and does not dispatch the submit event. Change the call to form.requestSubmit() or to form.requestSubmit(theIntendedButton).

requestSubmit(button) fails

Verify that the argument is a submit button associated with the same form. Query it from the form itself, as in the example above, and ensure the element has not been removed or replaced between lookup and submission.

Pressing Enter does nothing

Confirm that the input is inside the intended form and that application code is not preventing the event without performing its own save action. A prevented submit is not necessarily a Cypress failure; inspect the submit listener and the network or state update it is supposed to trigger.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A browser or test environment lacks requestSubmit()

Use an actual click on the intended submit button as a compatibility fallback, because clicking follows the user-facing validation and event path. Keep the markup free of the submit name/ID collision so the native method remains available where supported.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Patterns that prevent the bug from returning

  • Give controls semantic, specific IDs such as save-profile, continue-checkout, or delete-account.
  • Reserve method-like names, including submit, for JavaScript APIs rather than form-control names and IDs.
  • Keep one explicit submission function in application code and have both button clicks and keyboard submission reach it through the form’s submit event.
  • When a test needs to prove browser behavior, test both requestSubmit()-equivalent interaction (click or Enter) and any intentionally low-level submission path separately.
  • Assert the rendered structure in component tests so a future refactor cannot silently reintroduce name="submit" or id="submit".

Or skip the browser setup

If you need a clean image of the failing or corrected page for a bug report, visual regression record, or CI artifact, ScreenshotNeo can capture the URL through one request. It is separate from Cypress and does not repair form JavaScript, but it can document the rendered state without configuring a browser.

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and each response reports the page verdict and billing status. Its MCP server lets Claude, Cursor, or another MCP client call take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/profile -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/profile"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/profile' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo documentation for capture options and response headers. Create a free ScreenshotNeo account to get 1,000 screenshots each month without entering a card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

FAQ

Is this a Cypress defect?

No. Cypress can trigger the interaction that reaches your form code, but the method collision is produced by the page’s rendered form controls.

Can I keep a backend field named submit?

Only if you avoid exposing that name or ID as a descendant control of the form whose method you call. Renaming the control is safer than depending on a workaround.

When is direct submit() appropriate?

Only when deliberately bypassing constraint validation and the submit event is required. Most user-facing saves should use requestSubmit() or activate the intended submit button.

Frequently Asked Questions

Is this a Cypress defect?

No. Cypress can trigger the interaction that reaches your form code, but the method collision is produced by the page’s rendered form controls.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can I keep a backend field named submit?

Only if you avoid exposing that name or ID as a descendant control of the form whose method you call. Renaming the control is safer than depending on a workaround.

When is direct submit() appropriate?

Only when deliberately bypassing constraint validation and the submit event is required. Most user-facing saves should use requestSubmit() or activate the intended submit button.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.