Joins and reconciliation
Why can't I select subsidiary from transactionAccountingLine in SuiteQL?
Short answer
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.
The column does not exist on that table. Selecting it failed with a bare Unexpected Error that never names subsidiary or hints that a column is missing. Get subsidiary from transactionLine, joined on both transaction and line id.
Minimal reproduction
SELECT tal.subsidiary FROM transactionAccountingLine tal FETCH FIRST 1 ROWS ONLY
N/query.runSuiteQL error: Unexpected Error
That is the whole message. The N/query.runSuiteQL error: prefix is added by the module; everything after it is NetSuite's text, verbatim.
What proves the column is absent
The error does not prove it, only that the query failed. SELECT * is what proves it:
SELECT * FROM transactionAccountingLine FETCH FIRST 1 ROWS ONLY
The columns returned, in the order returned:
transactionline, exchangerate, lastmodifieddate, accounttype, amount, posting,
amountunpaid, processedbyrevcommit, paymentamountunused, accountingbook, amountlinked,
paymentamountused, debit, credit, amountpaid, transaction, netamount, account
No subsidiary anywhere. Columns that were null on the sampled row still came back as explicit JSON keys, so nothing was hidden by nullability.
That list is unusually safe to reuse. The same statement plus a twenty-row key union returned the same eighteen keys, with no additions and no omissions, and this table carries no custom fields at all. SELECT * on transaction and on transactionLine returns account-specific custom fields inline, which makes those column lists one account's; this one is not diluted that way.
The fix
SELECT tl.subsidiary, SUM(tal.amount) AS posted
FROM transactionAccountingLine tal
JOIN transactionLine tl ON tl.transaction = tal.transaction
AND tl.id = tal.transactionline
GROUP BY tl.subsidiary
Both parts of that ON clause are required. Joining on tal.transaction alone runs fine and multiplies the result, because every accounting line then matches every line of the same document (the loose join and the two-column join were run against the same data and compared), so the multiplier is roughly the average number of lines on your documents. Any total built on the loose join counts the same accounting line more than once.
tl.subsidiary is a real column on transactionLine, and one of the probes behind this page used it in a six-table join. One caveat if you go on to join the subsidiary table itself: JOIN subsidiary s ON s.id = tl.subsidiary is an inner join, so it drops every line whose line-level subsidiary is null.
Why this happens
transactionAccountingLine carries the accounting view of a line: amounts, account, book, posting flag. The dimension columns live on transactionLine, whose census includes subsidiary, department, class and location. That is a description of the observed split between the two column censuses, not a documented design statement.
The unhelpful error is the part worth remembering. NetSuite does have a specific message for an unresolvable identifier, but this query did not get it. Unexpected Error carries no offset, no token and no column name. Where the difference comes from was not established; the practical consequence is that you cannot debug this from the message and have to enumerate columns with SELECT *.
An unresolvable column is the most common cause of syntax error ... near: FETCH(1,NN, token code:0), but it is not the only error an unresolvable column produces. The reproduction at the top of this page is exactly that shape, an unknown column in a statement carrying a FETCH FIRST clause, and it returns Unexpected Error instead. So the rule to keep is the action, not the diagnosis: if SuiteQL blames your FETCH clause, delete the FETCH clause and run again, because the second error names the real problem.
Scope: one production account on NetSuite 2026.1. Every probe behind this page ran entirely through N/query.runSuiteQL; the finding was not tested on the REST endpoint.
How to tell if you are affected
If a query against transactionAccountingLine returns Unexpected Error, list the real columns before changing anything else:
SELECT * FROM transactionAccountingLine FETCH FIRST 1 ROWS ONLY
Compare the returned key set against every column your statement referenced. A name that is not in the key set is your bug, and no further step is needed. If every name is present, the column list is not your problem and something else in the statement is raising the error. If what you got instead was a syntax error pointing at FETCH, delete the FETCH FIRST clause and run again; the second error names the real problem.
How this was established
7 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
- Why does my transactionAccountingLine join return far more rows than expected?
You joined on transaction alone. transactionAccountingLine must match transactionLine on both transaction and line id. The transaction-only join matches every accounting line against every line of the same document, silently multiplying rows and any sum built on them.
- What does 'N/query.runSuiteQL error: Unexpected Error' mean in SuiteQL?
It carries no code, no position and no identifier name. Read it as "something here is unsupported" and bisect the query. In this run it came from three BUILTIN calls and from a column that does not exist.
- 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.
- How do I list the tables and columns in a NetSuite account from SuiteQL?
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.
From the blog
- Why Your NetSuite AP Aging Never Ties to the General Ledger
The AP Aging report and the accounts payable balance on the balance sheet disagree, and the difference is not a missing bill. Three specific traps in how NetSuite stores payables, and the SuiteQL that finds each one.