Skip to content

Fix line numbers in schema syntax errors and name the source - #838

Merged
oryan-block merged 3 commits into
masterfrom
bugfix/392
Oct 16, 2026
Merged

oryan-block merged 3 commits into
masterfrom
bugfix/392

Conversation

@oryan-block

@oryan-block oryan-block commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #392

Checklist

  • Pull requests follows the contribution guide
  • New or modified functionality is covered by tests

Description

When there's a syntax error in the second or a later schemaString, the error says something like Invalid syntax with offending token '(' at line 9 column 17 while the mistake is on line 2 of some file. The column is right, but the line counts every line of the strings before it, and nothing says which string it came from. That's what Spring Boot starter users hit, since the starter passes each .graphqls resource as its own schemaString. SchemaParserBuilder.schemaString() appended everything to one StringBuilder and parseDocuments() parsed it as a single unnamed source. Files added with file() were already parsed one at a time under their own name, but graphql-java's InvalidSyntaxException message only has the line and column, so the name was only on SourceLocation.sourceName.

Schema strings are now kept as a list, and each one is its own source in a single MultiSourceReader. They're still parsed as one document, so a definition split across calls and comment-only strings keep working, but lines in syntax errors and AST SourceLocations are now relative to the string they came from. That includes the schema <unknown>:N part of FieldResolverScanner's "No method found as defined in schema" message. There's also a new schemaString(string, sourceName) overload to name the source. When a syntax error comes from a named source or a file, it's rethrown as SchemaSyntaxException, an internal subclass of InvalidSyntaxException, with " in " appended to the message, e.g. Invalid syntax with offending token '!' at line 2 column 15 in InvalidSyntax.graphqls. Location and source preview are kept.

graphql-java 26.1's MultiSourceReader.getSourceAndLineFromOverallLine has a bug: for anything on the last line of the last source it returns the cumulative line (page) instead of overallLineNumber - previousPage. That covers every unexpected end of input error (e.g. a missing closing brace in the last file) and anything in a one-line last string. To work around it, when there's more than one source each one gets a trailing line break if it's missing, so only the end of input is left on that line. An end of input error (offending token <EOF>) then gets the line count of the last source as its line, in both the location and the message. The old line in the message is found with the same number formatting graphql-java uses, since it groups thousands ("1,103"). A single source (one file() or one schemaString) gets no extra line break, so its positions are the same as on master. I haven't reported the bug upstream yet, but once it's fixed there the workaround can go.

I didn't parse each schema string as its own document like files are. It would avoid the workaround, but a comment-only string fails to parse on its own with '<EOF>', so a commented-out .graphqls file would break app startup for starter users, and a definition split across calls would break too. Unnamed strings also don't get a made-up name like "schemaString #2", since that would put fake names in every AST node's SourceLocation. So an error in an unnamed string shows the line within that string but not which one. Which means starter users get per-file line numbers from this alone, but file names only once graphql-spring-boot (GraphQLJavaToolsAutoConfiguration / ClasspathResourceSchemaStringProvider) passes the resource name through schemaString(content, name). That's a follow-up in that repo. Also, SchemaSyntaxException uses InvalidSyntaxException's protected constructor, which graphql-java marks @Internal (its own subclasses use it too), so a future graphql-java release could break it.

Behaviour change: with several schemaString calls, line numbers in syntax errors and AST SourceLocations are relative to their own string instead of counted across all of them, and the same goes for the line in FieldResolverScanner's missing method message. Syntax errors from named sources (file() or the new overload) are now thrown as SchemaSyntaxException with " in " at the end of the message. It's still an InvalidSyntaxException, so existing catches work, but for named sources it replaces graphql-java's own subclasses (MoreTokensSyntaxException, ParseCancelledException). New public API: schemaString(String, String).

🤖 Generated with Claude Code

SchemaParserBuilder appended every schemaString to one StringBuilder
and parsed the result as a single unnamed source. A syntax error in
the second or later string was reported with a line number counted
across all strings, and nothing said which string it came from. This
is what Spring Boot starter users hit, since the starter passes each
.graphqls file as its own schemaString. Files added with file() were
already parsed with their name, but graphql-java's
InvalidSyntaxException message only contains the line and column, so
the name was only visible on the exception's SourceLocation.

Each schemaString is now its own source in one MultiSourceReader, so
they are still parsed as a single document, but line numbers in parse
errors and AST source locations are relative to the string they came
from. graphql-java's MultiSourceReader numbers the last line of the
last source from the start of the first source, so every source is
ended with a line break, which leaves only the end of input on that
line, and the line of an unexpected end of input error is made
relative to the last source before it is reported.

A new schemaString(string, sourceName) overload names the source, and
when a syntax error comes from a named source or file the exception
is rethrown as an InvalidSyntaxException subclass whose message ends
with "in <sourceName>". Spring Boot starter users get line numbers
relative to each file from this change alone, but file names only
once graphql-spring-boot passes them to the new overload.

Fixes #392

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
oryan-block and others added 2 commits October 16, 2026 13:53
- Keep schema strings as a named Source instead of a Pair
- Move the syntax error rewrite out of parseDocument into
  syntaxErrorInSource
- Find an unexpected end of input by its '<EOF>' token instead of
  comparing line counts. It is always on the last line of the last
  source, so its line is that source's line count
- Format the line with NumberFormat, which is what graphql-java's
  MessageFormat uses for numbers
- Fold the seven new tests into four that each cover a separate case

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@oryan-block
oryan-block merged commit 4d85da7 into master Oct 16, 2026
6 checks passed
@oryan-block
oryan-block deleted the bugfix/392 branch October 16, 2026 18:32
oryan-block added a commit that referenced this pull request Oct 17, 2026
- Fix the two compiler warnings for the java.util.Map and Object class
  literals in isPropertyMap
- Flatten handleInputObjectFields with early returns
- Give the three enum-without-class messages the same fix hint, so only
  the reason differs
- Pick the type kind in unexpectedTypeError without a triple negation
- Write the source name as "in <file>", like the syntax errors from #838
- Drop "javax." from the library class prefixes, nothing there is an
  input class
- Shorten the missing property warning
- Fold the ObjectNode method name test and the Map subclass enum error
  test into their neighbours

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrong Line Numbers in Parsing Errors

1 participant