Skip to content

Est. 2011ยทMicrosoft Partner 7033487ยทDelivery under 3 minยทSupport 7 days a week

Your vault is empty.

Free Fix 102

Error 102: Incorrect Syntax Near – Finding the Real Line in a Long Batch

12 min read Updated October 4, 2026 SQL Server

Fix it now

Error 102’s published text is “Incorrect syntax near” followed by the token the parser was looking at when it gave up. That token is where it stopped, not where you went wrong. The mistake is almost always earlier: a missing comma, an unclosed quote, a reserved word used as a name, or a batch separator that reset the line numbering.

Parses without executing, so you can iterate quickly

SET PARSEONLY ON;
GO
-- paste the batch here, then run
GO
SET PARSEONLY OFF;
GO
  1. Count lines from the last GO, not from the top of the file. GO is a batch separator the client tool acts on, and the server numbers each batch from one.
  2. Read the statement immediately before the reported position. A missing comma or an unterminated CASE in the previous statement is the single most common cause.
  3. Check the token the message names. If it is a word like user, key, plan or function, it is reserved and needs square brackets: [user].
  4. If the message is 105 rather than 102, a quotation mark was opened and never closed, and everything after it was swallowed as string text.
  5. If the failing text is built at run time, print the generated string instead of executing it and parse that. 8180 means the generated statement could not be prepared, not that your outer code is wrong.

In SQL Server Management Studio, Ctrl+F5 parses the current batch without running it, which does the same job as SET PARSEONLY ON without editing the script.

If the batch parses, you are done. If it still fails, the next section explains what the parser is doing and why the position it reports drifts.

Why it happens

Running a batch happens in stages. The parser first checks that the text is grammatically valid T-SQL, knowing nothing about whether any of the tables or columns exist. Name resolution then binds identifiers to real objects, and only after that is a plan compiled and executed. Error 102 comes from the first stage, which is why nothing in the batch runs – not even the statements above the faulty one. A syntax error is fatal to the whole batch.

The parser has no idea what you meant. When it reads a token that cannot legally follow the previous one, it stops and quotes the token it was looking at. Leave a comma out of a select list and the parser happily reads the next column name as an alias for the previous one, then complains about the word after that. This is why the reported position is usually one token past the real fault, and occasionally a whole statement past it.

The neighbouring codes narrow it usefully. 156 is “Incorrect syntax near the keyword”, so the parser is telling you the offending token is a keyword where an identifier or expression belonged – which usually means a reserved word used as a name. 105 is “Unclosed quotation mark after the character string”, which is unambiguous. 4145 is an expression of non-Boolean type where a condition was expected, the classic single = in a WHERE or IF. 137 is “Must declare the scalar variable”, which is what you get when a variable is used in a batch after the GO that ended its scope.

Batch separators change the arithmetic in two ways at once. GO is not T-SQL: the client tool splits the script on it and sends each piece separately, so line numbers restart at every batch, and variables declared before a GO no longer exist after it. The first effect makes the reported line number misleading; the second produces 137. The same script usually manages both.

The mistake is in the statement before the one named

You have this one if The reported line looks perfectly correct on its own, and the statement above it ends without a comma, a closing bracket or an END.

  1. Read upwards from the reported position until you find an incomplete construct: a select list missing a comma, a CASE without END, a BEGIN without a matching END, or an unbalanced parenthesis.
  2. Terminate statements with semicolons. It costs nothing and makes the parser fail much closer to the real problem.
  3. Reformat the suspect statement onto separate lines so the parser’s position maps to something you can see.

A reserved word or an odd identifier is being read as syntax

You have this one if 156 rather than 102, or a 102 quoting a word you use as a column or table name, and renaming that column makes it go away.

  1. Wrap the identifier in square brackets: SELECT [key], [plan] FROM dbo.[Order];
  2. Use brackets for anything containing a space, a hyphen or a leading digit.
  3. If you prefer double quotes, check that QUOTED_IDENTIFIER is ON for the connection. It is a per-connection setting and drivers differ in what they set it to, so verify rather than assume.

Microsoft maintains a reserved keywords list for T-SQL, and it is not identical between releases. A script written years ago can start failing after an upgrade because a word it used as a name became reserved.

A quote was never closed

You have this one if 105, or a 102 where the quoted token is a long stretch of your own script text.

  1. Look for an apostrophe inside a literal that was not doubled. 'O'Brien' must be written 'O''Brien'.
  2. Check for smart quotes pasted from a document or an email. They look almost identical to apostrophes and the parser rejects them.
  3. Use the editor’s syntax colouring: everything after the stray quote is coloured as string text, which shows where it began.

A condition is not a condition

You have this one if 4145 rather than 102, on an IF, a WHERE or a CASE.

  1. Read the message literally: an expression of non-Boolean type was supplied where a condition is expected.
  2. The usual cause is a bare column or a variable where a comparison belongs, or a single = intended as IS NOT NULL.
  3. Write the comparison out in full rather than relying on a value being treated as true or false, because T-SQL does not do that.

The failing text is dynamic SQL

You have this one if 8180, statements could not be prepared, and the line number makes no sense against your source.

  1. Replace the execution with a PRINT of the string, run it, and inspect the generated text.
  2. Paste the generated text into a new window and parse it there, where the reported line number is meaningful.
  3. Use sp_executesql with typed parameters instead of concatenating values into the string. It removes a whole class of quoting faults and the injection risk with them.

Full reference

Match the message to the mistake

Code Published text What it usually means
102 Incorrect syntax near ‘<token>’ The parser stopped there. The fault is at or before it
156 Incorrect syntax near the keyword ‘<word>’ A reserved word where an identifier or expression belonged
105 Unclosed quotation mark after the character string ‘<text>’ A quote was opened and never closed
137 Must declare the scalar variable “<name>” A variable used after the GO that ended its scope, or never declared
4145 An expression of non-boolean type specified in a context where a condition is expected A value where a comparison belongs
8180 Statement(s) could not be prepared Usually a dynamic SQL string that failed to compile

Making the parser fail closer to the fault

  • Terminate every statement with a semicolon. The parser then has a clear boundary and stops at the statement that is wrong rather than the next one.
  • Keep batches short with regular GO separators, so a reported line number is close to something you can find.
  • Indent consistently, and put each column of a long select list on its own line. A missing comma is visible immediately in that layout and invisible in a wrapped one.
  • Parse before running, with Ctrl+F5 or SET PARSEONLY ON, so you iterate on syntax without side effects.
  • Bracket every identifier that is not a plain word, rather than only the ones that have failed so far.

Dynamic SQL, properly

Print first, then execute with typed parameters

DECLARE @sql nvarchar(max) =
    N'SELECT * FROM dbo.Orders WHERE CustomerId = @cust AND OrderDate >= @from;';

PRINT @sql;   - read it before you run it

EXEC sys.sp_executesql
     @sql,
     N'@cust int, @from date',
     @cust = 42,
     @from = '2026-01-01';

Concatenating values into a statement is where most 102s in generated code come from: a value containing an apostrophe closes the literal, and the rest of your statement is read as string text. sp_executesql takes the parameters separately, so quoting stops being your problem, and it lets the plan be reused rather than compiled per value. It also removes the injection risk, which is the same fault seen from a security angle rather than a syntax one.

Why line numbers lie

Situation Effect on the reported position
A GO earlier in the file Line numbers restart from 1 in the new batch
A missing comma in a select list The parser reads the next name as an alias and fails one token later
An unclosed quote Everything after it is string text, so the position can be pages away
An unbalanced parenthesis or a missing END The parser reaches the end of the text still expecting more, so the position is the last line
Dynamic SQL The position refers to the generated string, not to your source file

Scripts that used to work

A script that ran for years and now fails on a new server has almost always met one of two changes. Either a word it uses as an identifier became reserved in a later release, which brackets fix permanently, or the database moved to a higher compatibility level that no longer tolerates a deprecated construct. Check SELECT name, compatibility_level FROM sys.databases; on both servers before assuming the script is at fault. Some constructs require a minimum compatibility level rather than only a minimum product version, so an upgraded server hosting a database still at an old level can reject syntax the same server accepts elsewhere.

None of this is a product problem

Error 102 behaves identically on Express and on Enterprise. No edition, patch, licence or configuration option changes how the parser reads a batch, and there is nothing to buy. If someone is selling you a fix for a syntax error, the thing being sold is a text editor.

Every code this article covers

Code What it points at Source
102 Incorrect syntax near the quoted token: the parser stopped there because that token cannot legally follow what came before Microsoft Learn
156 Incorrect syntax near the named keyword: a reserved word appeared where an identifier or expression was expected Microsoft Learn
105 Unclosed quotation mark after the quoted character string Microsoft Learn
137 Must declare the scalar variable: the variable was never declared, or its declaration was in a batch that GO has already ended Microsoft Learn
4145 An expression of non-Boolean type was supplied where a condition is expected, near the quoted token Microsoft Learn
8180 Statements could not be prepared, most often a dynamic SQL string that failed to compile Microsoft Learn

Confirm the fix worked

  1. Parse the whole script without executing it, with Ctrl+F5 or SET PARSEONLY ON, and confirm it reports success.
  2. Run it against a test database and confirm the statements before the previously failing one now execute.
  3. For dynamic SQL, print the generated string once more and confirm it parses on its own.
  4. Confirm no 137 remains, which would mean a variable is still being used across a GO.
  5. Run it on the target server as well as your own, in case a compatibility level or reserved word differs.

Questions people ask about this

Why does the line number point at a blank line?

Because the parser reached the end of the text still expecting more, typically an unclosed bracket, an unterminated string, or a BEGIN with no END. Count lines from the last GO and read upwards.

Does this cost anything to fix?

No. Error 102 is a text problem in your own script and it behaves identically on every edition. No edition, patch or licence changes how the parser reads a batch.

Why did a working script start failing after a server upgrade?

Usually a word it uses as an identifier became reserved, or the database moved to a higher compatibility level that no longer tolerates a deprecated construct. Bracket your identifiers and compare compatibility levels between the two servers.

What is the difference between 102 and 156?

156’s published text names a keyword specifically: incorrect syntax near the keyword. So it is telling you the token it stopped at is a reserved word, which usually means you have used one as a column or table name. 102 is the general form.

Can I make errors point closer to the real fault?

Yes. Terminate every statement with a semicolon, keep batches short with regular GO separators, put each column of a select list on its own line, and parse before running. All four narrow the gap between where the parser stops and where the mistake is.

Related error codes

Was this article helpful?

Your feedback helps us improve our documentation.

Related articles

License Error Error 33111: Cannot Find Server Certificate When Restoring a TDE Backup Free Fix Error 7399 and 7303: Linked Server Provider and Authentication Failures Free Fix Error 14274: Jobs Break After a Server Rename or an MSX Move Free Fix Error 3414 and 3417: Recovery Failed and the Instance Will Not Start
โ† Back to Knowledge Base