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

The usual cause is a value mismatch: Cypress’s cy.log() command prints a message but yields null. It does not return a DOM element or text object. If code then calls .text() on the value yielded by cy.log(), JavaScript is effectively calling null.text(), which throws the error. Query the element first, extract its text, and only then pass the string to cy.log().

The error location can be misleading. A callback containing cy.log() may be where the exception surfaces, while the actual null is the object before .text(). Read the stack trace’s exact failing expression before changing the test.

What the error actually means

Cypress DOM queries such as cy.get() yield jQuery-wrapped elements. Those objects expose jQuery methods including .text(). By contrast, the documented subject yielded by cy.log() is null; it is a Command Log side effect, not a subject-producing query. It must be started from cy, and assertions cannot be chained to it.

// This is invalid: cy.log() yields null
cy.log('message').text()

// This is also conceptually wrong
const value = cy.log('message')
value.text()

When JavaScript reports “cannot read property text of null” (or the equivalent modern “cannot read properties of null”), the immediate question is: which expression is null? It might be the result of cy.log(), a missing variable, a DOM lookup that returned no element, or a native API such as querySelector(). The phrase alone does not identify the source.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Safely read and log an element’s text

Use invoke('text') for a string

Let Cypress find the element, invoke jQuery’s text() method on the yielded object, and log the resulting string in a following callback:

cy.get('.message')
  .invoke('text')
  .then((text) => {
    cy.log(text)
  })

invoke('text') remains in the Cypress command chain. The .then() callback receives the extracted string, so cy.log(text) logs a string and does not need to yield anything for the next operation.

Read with a .should() callback

When the text may appear after rendering, a .should(callback) callback can read the jQuery object and make retryable assertions:

cy.get('.message').should(($element) => {
  const text = $element.text()
  expect(text).to.contain('Expected message')
})

Cypress can retry the query and callback while the assertion is failing. Keep the callback free of Cypress commands; do not put cy.log(), cy.get(), or another cy.* command inside it.

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

Use native innerText only when that is what you need

jQuery .text() returns the text content of the matched node and descendants. If your test specifically requires the browser’s rendered innerText behavior, obtain the first DOM element from the jQuery object:

cy.get('.message').should(($element) => {
  const node = $element.get(0)
  expect(node).to.exist
  expect(node.innerText).to.contain('Expected message')
})

Use innerText deliberately: it reflects rendered text and can differ from text content because of visibility and layout.

Choose the right Cypress chain

Goal Recommended pattern Why
Verify exact text cy.get(selector).should('have.text', expected) Query and assertion are retried while the condition is pending.
Verify contained text cy.get(selector).should('contain.text', expected) Useful when the element includes additional text.
Make several retryable checks cy.get(selector).should(($el) => { ... }) Read $el.text() and perform synchronous assertions; no Cypress commands inside.
Log a value once .invoke('text').then((text) => cy.log(text)) .then() is for one-time dependent work after the value is available.

Exact and partial assertions

// Exact text (including whitespace as Cypress evaluates it)
cy.get('.status').should('have.text', 'Complete')

// Substring
cy.get('.status').should('contain.text', 'Complete')

Prefer these built-in assertions when logging is only being used to inspect a value. They keep the test focused and preserve Cypress’s retry behavior.

Why Cypress commands cannot run inside .should()

A .should() callback may execute repeatedly until its assertions pass or the command times out. Cypress therefore prohibits Cypress commands inside that callback. Calling cy.log() there can produce a command-queue error, even if the logged value itself is valid.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
// Invalid: Cypress command inside a retryable callback
cy.get('.message').should(($el) => {
  cy.log($el.text())
  expect($el).to.contain.text('Ready')
})

Move logging after the retryable assertion:

cy.get('.message')
  .should('contain.text', 'Ready')
  .invoke('text')
  .then((text) => {
    cy.log(`Message: ${text}`)
  })

Here the assertion can retry until “Ready” appears. Only after it succeeds does Cypress invoke the one-time logging callback.

Understand Cypress’s queued, asynchronous execution

Cypress commands are queued; they do not return the final browser value synchronously. This code does not assign the element text to text:

// Wrong: this runs before the queued command has produced text
let text
cy.get('.message').invoke('text').then((value) => {
  text = value
})
cy.log(text) // text is still undefined here

Keep dependent work inside the chain:

cy.get('.message')
  .invoke('text')
  .then((text) => {
    cy.log(text)
    // use text here, where it exists
  })

If a later condition must eventually become true, keep the query and assertion together in a retryable chain. Inserting .then() before the condition ends the automatic query-retry chain for that segment.

Debug the specific null value

  1. Read the code frame in the stack trace. Identify the exact .text() call and the expression immediately before it.
  2. Check for a cy.log() subject mistake. Remove code that chains .text(), assertions, or DOM methods from cy.log().
  3. Check the selector. Confirm that cy.get(selector) matches the intended element and that the selector is scoped to the correct container.
  4. Check render timing. A late-rendered element should be queried and asserted in a Cypress chain, not located once with a synchronous browser API.
  5. Check variables and aliases. Ensure a value passed into .text() was assigned in a preceding .then() or yielded by the current command.
  6. Reduce the test. Keep only the visit, the query, the text operation, and the failing assertion. A minimal reproduction makes command-queue and application-timing issues visible.

Typical failure patterns and repairs

Symptom Likely cause Repair
cy.log(value).text() throws cy.log() yields null. Obtain value from cy.get(...).invoke('text') first.
$el.text() throws The callback argument is not the expected jQuery object, or a native lookup returned null. Verify the yielding command and guard native lookups with an existence assertion.
Logging inside .should() fails Cypress commands are prohibited in retryable callbacks. Assert inside .should(); log in a following .then().
Text is empty intermittently The element exists before its content is rendered. Use .should('have.text', ...), contain.text, or a retryable callback.
Assertion runs before a variable is set Synchronous code was placed outside the Cypress chain. Move all dependent statements into .then() or another chained command.

Whitespace, multiple elements, and hidden content

If a selector matches multiple nodes, jQuery’s .text() combines descendant text. Assert against the combined result only when that is intentional; otherwise narrow the selector or use .eq(index):

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.
cy.get('.row .message').eq(0).invoke('text').then((text) => {
  cy.log(text.trim())
})

Trimming for display can make logs easier to read, but do not trim when whitespace is part of the behavior being tested. For rendered visibility semantics, use innerText as shown earlier. For hidden nodes, decide whether the requirement is DOM text or user-visible text before choosing the API.

Timeouts, browsers, and environment differences

Cypress retries DOM queries and assertions until they pass or the configured timeout expires. A timeout usually means the selector, application state, or expected text is wrong—not that increasing the timeout will fix a null subject. First confirm that the page reached the expected state and that network data or feature flags are consistent.

If the minimal test behaves differently across browsers or CI, compare the browser, viewport, authentication state, and application build. A different rendering path can change when text is inserted or whether innerText includes it. Preserve the smallest failing example when asking for help and include the complete stack-trace line.

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

“Or skip the browser setup”

If your goal is a repeatable page image rather than an interactive Cypress assertion, ScreenshotNeo provides a single HTTP request for a PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

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

For developers and AI workflows, its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. Options include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, PDF paper settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification.

See the ScreenshotNeo documentation for parameters and authentication. The same request can be run from a shell:

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

Python:

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

Node.js:

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

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to try it.

FAQ

Does cy.log() return the text it prints?

No. It prints to the Cypress Command Log and yields null. The text must come from a DOM query or another value before logging.

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

Can I call cy.log() inside a .should() callback?

No. Cypress prohibits Cypress commands inside a .should() callback because the callback can be retried. Log in a following .then().

When should I use .text() instead of innerText?

Use jQuery .text() for DOM text content. Use native innerText when your assertion specifically concerns rendered, user-visible text.

Frequently Asked Questions

Why does the stack trace point at a logging callback?

The callback is where the null value is used, but the null may have been produced by an earlier query or variable assignment. Inspect the exact expression on the failing line.

Will increasing Cypress’s timeout fix this error?

Only if the underlying issue is a late-rendered element and the query remains in a retryable Cypress chain. It cannot make cy.log() yield a DOM object or repair a wrong selector.

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

The Bottom Line

Get text from the element yielded by Cypress, assert it with a retryable command when necessary, and treat cy.log() as a terminal logging action that yields null.

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.