Filters and criteria
Why is equalto not a valid operator on internalid in a saved search filter?
Why is equalto not a valid operator on internalid in a saved search filter (SSS_INVALID_SRCH_OPERATOR)?
Because NetSuite does not accept equalto on internalid: the filter is refused with SSS_INVALID_SRCH_OPERATOR and the search returns an error instead of rows. Use anyof, which takes an array and scales to a list. is also works, on a bare string, which is the part most people do not expect.
Because NetSuite does not accept equalto on internalid. The filter object itself is refused and the search returns an error instead of rows. Use anyof.
The rejection, verbatim:
SSS_INVALID_SRCH_OPERATOR
An nlobjSearchFilter contains an invalid operator, or is not in proper syntax: internalid.
Note that the message names the field, not the operator. It tells you where the problem is and not what it is, which is why people go looking for a broken join or a permissions issue on a filter that is one word away from working. That text is also the whole of what the engine says about it: this run measured which spellings are accepted, not NetSuite's reason for refusing this one.
Minimal reproduction
Four filters on the same transaction search, one internal id, one call each:
internalid anyof ['2'] -> rows
internalid is '2' -> the same rows
internalid equalto '2' -> SSS_INVALID_SRCH_OPERATOR
internalid noneof ['2'] -> every other row, exactly
Of the three natural spellings of "this id and no other", two work and the SQL-looking one does not. That is the whole finding. equalto is the operator most people reach for by analogy with SQL, and it is the one that fails.
Use anyof
anyof takes an array, so it is the form that scales from one id to a list without a rewrite:
internalid anyof ['2']
internalid anyof ['2','3']
Adding the second id returned the lines of both transactions and nothing else. Nothing about the filter changes shape as the list grows, which is the practical argument for making anyof your default even when you only have one value in hand today.
is also works, and that is worth stating plainly because it contradicts a reasonable expectation. internalid behaves like a select-type field, and you would expect such a field to want an array rather than a bare string. Here the bare string is accepted, and it returned the same rows as anyof did for the same single id. So is is not wrong, it just does not scale.
noneof is the clean complement. Measured against the whole search with a summary column, the row count it returned was the search's own total minus the lines of the excluded transaction, to the row. No off-by-one, nothing dropped. If you want everything except a handful of ids, noneof behaves the way you would hope.
Counting the rows of a transaction search takes a little care, because one transaction is several lines and a summary Count does not return what you expect. That is a separate page.
How to tell if you are affected
This one is loud, which makes the audit easy. Grep your SuiteScripts and your integration code for equalto in the same expression as internalid, or for a search filter list assembled from a config where the operator is a string somebody typed. Any of them that ran would have thrown SSS_INVALID_SRCH_OPERATOR at the point of execution, so if a script is running in production it is not doing this.
The failure to actually worry about is the silent cousin. Swap the field for a text formula and every operator becomes a string operator, so contains on an internal id turns into a substring scan over the digits, returns a plausible number of rows and raises nothing at all. That mistake reaches production, where this one cannot. It has its own page.
Scope
One sandbox account on NetSuite 2026.1, Administrator role, transaction search, one internal id and one pair of ids. These searches were built and run ad hoc through the SuiteScript search engine (nlapiCreateSearch / runSearch) without saving anything, so the error above is the search engine's, raised on the filter object. The run did not enumerate the operator dropdown that the saved search form offers for Internal ID in the browser, so this page does not claim what that dropdown lists.
How this was established
1 probe run against one NetSuite 2026.1 sandbox account on the Administrator role, through the saved search UI and the SuiteScript search engine. Where this page draws a boundary around a finding, that boundary is the edge of what was actually run.
Related
- Why does a Formula (Text) criteria on internal id match far too many rows?
Because a text formula makes every operator a string operator. Formula(Text) {internalid} contains '2' is a substring scan over the digits of the id, so it matches 2, 12, 20, 102 and everything else containing a 2. In the account tested it matched more than half the rows in the search, with no error. Use anyof, or Formula(Numeric).
- Why does {entity.internalid} not work in a NetSuite saved search formula?
Because the token is {entity.id}. In a formula, internalid is not a resolvable name, and the failure is silent: the column fills with the literal string ERROR: Field Not Found on every row and the search reports success. One join level works with .id; two levels did not resolve in this run.
- How do I count the number of rows a NetSuite saved search returns?
Add a Formula(Numeric) results column whose formula is the literal 1 and set its Summary to Sum. That total is the row count. Count does not give you one: it is COUNT(DISTINCT), so Count of {internalid} returns distinct transactions and Count of a constant returns 1.