Error messages, decoded
How do I list the tables and columns in a NetSuite account from SuiteQL?
Short answer
You cannot. OA_TABLES and OA_COLUMNS are rejected with "Invalid search type", and ALL_TABLES, USER_TAB_COLUMNS and ALL_TAB_COLUMNS are absent too. For columns, run SELECT * with FETCH FIRST 1 ROWS ONLY; for custom records, query customrecordtype.
You cannot, not from SuiteQL itself. Every standard catalog view tried here was rejected. Discover columns with SELECT * limited to one row, and discover custom record types by querying customrecordtype. Everything else has to come from the Records Browser.
Minimal reproduction
SELECT * FROM OA_TABLES FETCH FIRST 20 ROWS ONLY
N/query.runSuiteQL error: Invalid search type: OA_TABLES
OA_COLUMNS gives Invalid search type: OA_COLUMNS. ALL_TABLES, USER_TAB_COLUMNS and ALL_TAB_COLUMNS each give the same message with their own name. Adding a WHERE clause changes nothing: SELECT * FROM OA_TABLES WHERE table_name LIKE '%BUILTIN%' returned the error byte for byte identical to the bare form.
DUAL is the one Oracle-style oddity that survives: SELECT * FROM DUAL returns a single row with a single column dummy set to "X".
The fix: enumerate columns one table at a time
SELECT * FROM transaction FETCH FIRST 1 ROWS ONLY
The returned object's key set is the column list for that row. Columns that are null on that row come back as explicit keys with a null value rather than being omitted, which is the reason one row is worth anything at all. On the ten tables checked this way, the single-row key set matched the union of keys over 20 rows every time. The run records that as an observation and not as a schema guarantee, so on a wide, sparsely populated table, treat the one-row list as a strong lead rather than a certified schema.
Two things that output makes obvious and no documentation will: custom fields are interleaved with standard ones with no special syntax and no separate catalog lookup, and roughly two thirds of the columns on transaction in a mature account were account-specific custom fields. Any column reference you copy from someone else's account is a guess.
A wrong guess does not announce itself either. With a FETCH FIRST clause in the statement, an unresolvable column is most often reported as syntax error, state:0(10102) near: FETCH(1,NN, token code:0), blaming a clause that is fine. It is not the only outcome: the same shape returned a bare Unexpected Error in one run. So follow the action rather than the diagnosis: if SuiteQL blames your FETCH clause, delete the FETCH clause and run again, because the second error names the real problem. See the FETCH page.
A custom field found this way is queryable directly by its field id, with no join. Substitute a field id from your own SELECT * output for the one the run happened to pick:
SELECT custbody1 FROM transaction FETCH FIRST 1 ROWS ONLY
The fix: enumerate custom record types
SELECT scriptid, internalid FROM customrecordtype FETCH FIRST 25 ROWS ONLY
customrecordtype is directly queryable. Take a scriptid from that result and use it as a table name in lowercase, for example SELECT * FROM customrecord_agile_sprint FETCH FIRST 1 ROWS ONLY on the record type the run happened to pick, and combine it with the one-row trick above to get that record's columns.
Take the whole list in one call, because there is no second page to fetch. SQL OFFSET is silently discarded here. SELECT id FROM transaction ORDER BY id OFFSET 500 ROWS FETCH NEXT 5 ROWS ONLY returned the same five rows as the same statement with OFFSET 0, no error, no warning. Through N/query.runSuiteQL there is no URL parameter to fall back on, so raise the FETCH FIRST number rather than trying to page.
Why this happens
Observed, not inferred: five catalog view names were tried and all five were rejected with Invalid search type, while ordinary tables in the same session resolved normally. The error text names the view as a search type, which is consistent with SuiteQL resolving FROM targets against NetSuite record and search types rather than against a SQL data dictionary.
Where inference begins: this does not prove no catalog exists anywhere in NetSuite, only that these five names are unreachable from this surface. The same gap kills any attempt to enumerate the BUILTIN functions, since the only route tried was OA_TABLES.
How to tell if you are affected
Run the reproduction in your own account. It takes seconds and the answer is unambiguous.
SELECT * FROM OA_TABLES FETCH FIRST 20 ROWS ONLY
If it returns Invalid search type: OA_TABLES, you are on the same surface as this run, where the other four names fail the same way. If it returns rows, your account exposes something this one did not, and that is worth writing down.
Scope: one production account on NetSuite 2026.1. Everything quoted here ran through N/query.runSuiteQL, and none of the five catalog views was tried on the REST endpoint, so this page says nothing about how that surface answers. The error strings above are NetSuite's own text verbatim.
How this was established
15 probe runs against one live NetSuite 2026.1 account on the Administrator role, through N/query.runSuiteQL. Where this page draws a boundary around a finding, that boundary is the edge of what was actually run.
Related
- What does SuiteQL mean by Unknown identifier 'x'. Available identifiers are: {transaction=transaction}?
The column does not exist on that table. Despite the wording, "Available identifiers" lists the table aliases in scope, not the columns you could have used. Adding a FETCH FIRST clause can hide this error behind a syntax error.
- SuiteQL says syntax error near FETCH, but my FETCH FIRST clause is fine. What is wrong?
Your FETCH clause is probably fine. A column that does not exist on the table is the usual cause. Delete the FETCH FIRST clause, run the statement again, and the second error names the real problem.
- Why does SuiteQL OFFSET return the same rows as page 1?
The SQL clause OFFSET n ROWS FETCH NEXT m ROWS ONLY has its OFFSET silently discarded on both engines, even with an explicit ORDER BY. Every page returns the first rows again. Paginate with the REST ?offset= parameter instead.
- Why can't I select subsidiary from transactionAccountingLine in SuiteQL?
There is no subsidiary column on transactionAccountingLine, and selecting it fails with a bare "Unexpected Error" that never names the column. Join transactionLine on both transaction and line id, then read subsidiary from there.
- What does the SuiteQL error 'Invalid or unsupported search' mean?
A generic refusal carrying no error code, no position and no identifier name. In this run it came from CAST(id AS VARCHAR(20)), a date column compared to a bare ISO string, GROUPING SETS, and one RIGHT JOIN.
From the blog
- NetSuite Field Explorer vs MokuBot: Two Different Questions
Field Explorer tells you what a field is called. MokuBot uses the field. An honest comparison of the two, including where Field Explorer is still the better tool.