Syntax error: Expected end of input but got keyword
BigQuery read a complete statement and then found more text. The position is the first token that does not belong. The common causes are a second statement without a semicolon, a clause in the wrong order, and a stray keyword. Which token the message names tells you which.
Syntax error: Expected end of input but got keyword SELECT at [2:1]
What BigQuery means
The parser read a whole statement, found nothing in the grammar that could continue it, and then met another token before the end of the script. [2:1] is line 2, column 1: the first token that does not belong.
The mistake is usually just before that token, not at it. After LIMIT 1 the parser is happy, so it cannot say anything about LIMIT. It only learns something is wrong when it reaches the next word.
The message names the kind of token. It is the quickest way to the cause:
| The message says | Look for |
|---|---|
got keyword SELECT (or another statement keyword) | a second statement with no ; before it |
got keyword WHERE, JOIN, GROUP, ORDER, UNION | a clause in the wrong place. Something earlier already ended the query. |
got identifier "c" | a missing comma, or a misspelled keyword read as an alias |
got "-", got ")", got ",", got "@" | a character that cannot continue the statement |
got string literal 'x', got integer literal "2" | two values side by side, with an operator or comma missing |
The smallest query that reproduces it
SELECT 1
SELECT 2
Syntax error: Expected end of input but got keyword SELECT at [2:1]
The fix
A second statement without a semicolon. End the first one:
SELECT 1 AS a;
SELECT 2 AS b
This runs as a script of two statements. The same error appears when a statement that ends in a query, such as CREATE TEMP TABLE x AS SELECT 1 a, is followed by another statement with no ;. Inside a BEGIN ... END block the message is different: Syntax error: Expected ";" but got keyword SELECT at [3:1].
A clause after one that ends the query. A SELECT has a fixed order: FROM (with its joins), WHERE, GROUP BY, HAVING, QUALIFY, WINDOW, ORDER BY, LIMIT. Once you write a later clause, an earlier one cannot come back. These all fail the same way:
SELECT * FROM UNNEST([1]) AS a LIMIT 1 WHERE a = 1
Syntax error: Expected end of input but got keyword WHERE at [1:40]
| You wrote | Message names | Fix |
|---|---|---|
... LIMIT 1 WHERE ... | keyword WHERE | WHERE goes before LIMIT |
... ORDER BY a GROUP BY a | keyword GROUP | GROUP BY goes before ORDER BY |
... HAVING a = 1 WHERE ... | keyword WHERE | WHERE goes before GROUP BY |
... LIMIT 10 OFFSET 5 ORDER BY a | keyword ORDER | ORDER BY goes before LIMIT |
... WHERE a = 1 JOIN ... | keyword JOIN | joins belong to FROM, before WHERE |
... LIMIT 1 OFFSET 1 LIMIT 2 | keyword LIMIT | a query has one LIMIT; OFFSET follows it |
Move the clause to where it belongs:
SELECT * FROM UNNEST([1, 2]) AS a JOIN UNNEST([2]) AS b ON a = b WHERE a = 1
If the clause genuinely has to run after a LIMIT, put the limited query in a subquery and continue outside it:
SELECT *
FROM (SELECT * FROM UNNEST([1, 2]) AS a ORDER BY a LIMIT 1)
WHERE a = 1
A set operation after a LIMIT. LIMIT applies to a whole UNION ALL chain, so the first query cannot have its own without parentheses:
(SELECT a FROM UNNEST([1, 2]) AS a LIMIT 1)
UNION ALL
SELECT 3
An ORDER BY and LIMIT at the very end apply to the combined result: SELECT a FROM ... UNION ALL SELECT 3 ORDER BY a LIMIT 2 is valid.
A stray word. A keyword that belongs to no clause at that point:
SELECT 1 DISTINCT
DISTINCT has to follow SELECT directly. Likewise CASE WHEN 1=1 THEN 2 END END stops at the second END.
A missing comma. Two names next to each other read as a AS b. A third name has nothing to attach to:
SELECT a b c FROM (SELECT 1 a, 2 b, 3 c)
Syntax error: Expected end of input but got identifier "c" at [1:12]
A missing comma before a dotted name fails at the dot instead. SELECT a then t.b on the next line reports got ".".
A character that does not belong. Extra closing brackets give got ")", as in SELECT (1 + 2)) and WITH t AS (...) SELECT * FROM t) SELECT 2. A doubled comma gives got ",". A dash in an unquoted name gives got "-": SELECT 1 AS my-col. Backtick the name, AS `my-col` , or rename it.
The variants of the message
Every line below was produced by BigQuery:
Syntax error: Expected end of input but got keyword SELECT at [2:1]
Syntax error: Expected end of input but got keyword WHERE at [1:40]
Syntax error: Expected end of input but got keyword JOIN at [1:40]
Syntax error: Expected end of input but got keyword UNION at [1:40]
Syntax error: Expected end of input but got keyword DISTINCT at [1:10]
Syntax error: Expected end of input but got keyword AS at [1:32]
Syntax error: Expected end of input but got identifier "c" at [1:12]
Syntax error: Expected end of input but got string literal 'x' at [1:10]
Syntax error: Expected end of input but got integer literal "2" at [1:10]
Syntax error: Expected end of input but got ")" at [1:10]
Syntax error: Expected end of input but got "-" at [1:15]
Syntax error: Expected end of input but got "," at [1:23]
Syntax error: Expected end of input but got "@" at [1:10]
Queries that look wrong but run
A trailing comma before FROM. SELECT 1 AS a, FROM UNNEST([1]) is valid. So is a trailing comma at the end of a select list with no FROM.
A semicolon at the end. SELECT 1; is fine. A doubled ;; is not: Syntax error: Unexpected ";".
A #standardSQL first line. It parses.
Two string literals side by side. SELECT "a" "b" AS s returns ab. BigQuery joins adjacent string literals, so no error appears if you forget a comma between two quoted values. You get one value where you wanted two.
Different errors that look like this one
Syntax error: Unexpected keyword FROM. A word that cannot start or continue what the parser is reading, such as FROM FROM or a comma before FROM in a FROM list. See Unexpected identifier for the same family.
Syntax error: Unexpected end of script. The statement stops in the middle: WHERE a = 1 AND with nothing after.
Syntax error: Expected keyword ALL or keyword DISTINCT but got keyword SELECT. A bare UNION. Write UNION ALL or UNION DISTINCT.
Syntax error: Function call cannot be applied to this expression. Function calls require a path, e.g. a.b.c(). A bracket straight after a value, as in WHERE a IN (1, 2) (3).
Syntax error: WHERE not supported after FROM query; Consider using pipe operator `|> WHERE` . A query that starts with FROM is a pipe query. See WHERE not supported after FROM query.
Syntax error: Trailing comma after the WITH clause before the main query is not allowed. A comma after the last CTE.
In Querylab.io
Querylab.io parses in the browser as you type. These are marked as errors with BigQuery’s wording, at the same token as BigQuery: a second statement without ;, WHERE after LIMIT, GROUP after ORDER BY, WHERE after HAVING, JOIN after WHERE, UNION after LIMIT, a stray DISTINCT, an extra ), a stray string or integer literal, and a doubled END. For a second statement with no ; the editor keeps checking the rest of the script.
Two cases were not marked. LIMIT 10 OFFSET 5 ORDER BY a and SELECT 1 AS my-col showed no diagnostics, so only BigQuery’s dry run reports them, when you pause. A doubled comma (SELECT 1 AS a, 2 AS b,, 3) is marked with the editor’s own words, Expected expression before comma in SELECT list.
There are quick fixes for one cause, a missing comma before a dotted name that starts the next line, as in a then t.b on the following line. The editor’s lightbulb offers it from the dry-run error, and after a failed run the status bar offers it as a button. The other causes have no fix, so repair them by hand with the table above.