Repository: graphql-java/graphql-java (language: java)

Issue:
AstPrinter produces invalid GraphQL
**Describe the bug**
When a non-BlockString string contains escaped characters like newlines or quotes, AstPrinter does not escape them and the resulting document is not valid GraphQL.

**To Reproduce**
https://github.com/pcarrier/graphed/blob/main/src/jvmTest/kotlin/com/pcarrier/graphed/graphql/Repro1.kt

```
=== Original ===
"\"" scalar A
=== Parsed and printed ===
"""
scalar A

Exception in thread "main" graphql.parser.InvalidSyntaxException: Invalid syntax with ANTLR error 'token recognition error at: '"""\nscalar A\n'' at line 1 column 1
	at graphql.parser.Parser$1.syntaxError(Parser.java:368)
…
```


Discussion / hints:
Hello from the future! I wanted to say, thanks for reporting the bug! I'm working on a fix
Hello, this issue has been inactive for 60 days, so we're marking it as stale. If you would like to continue this discussion, please comment within the next 30 days or we'll close the issue.
Trialing our new bot

This is still on my todo list

Fix patch:
diff --git a/src/main/java/graphql/GraphQLError.java b/src/main/java/graphql/GraphQLError.java
index b4fc6fa600..c18752b14a 100644
--- a/src/main/java/graphql/GraphQLError.java
+++ b/src/main/java/graphql/GraphQLError.java
@@ -39,8 +39,10 @@ public interface GraphQLError extends Serializable {
     ErrorClassification getErrorType();
 
     /**
-     * The graphql spec says that the (optional) path field of any error should be a list
-     * of path entries https://spec.graphql.org/October2021/#sec-Handling-Field-Errors
+     * The graphql spec says that the (optional) path field of any error must be
+     * a list of path entries starting at the root of the response
+     * and ending with the field associated with the error
+     * https://spec.graphql.org/draft/#sec-Errors.Error-Result-Format
      *
      * @return the path in list format
      */
diff --git a/src/main/java/graphql/language/AstPrinter.java b/src/main/java/graphql/language/AstPrinter.java
index 218dabd59e..5070a4f139 100644
--- a/src/main/java/graphql/language/AstPrinter.java
+++ b/src/main/java/graphql/language/AstPrinter.java
@@ -534,7 +534,7 @@ private String description(Node<?> node) {
         if (description.isMultiLine()) {
             s = "\"\"\"" + (startNewLine ? "" : "\n") + description.getContent() + "\n\"\"\"\n";
         } else {
-            s = "\"" + description.getContent() + "\"\n";
+            s = "\"" + escapeJsonString(description.getContent()) + "\"\n";
         }
         return s;
     }


Test patch:
diff --git a/src/test/groovy/graphql/parser/ParserTest.groovy b/src/test/groovy/graphql/parser/ParserTest.groovy
index 4484eb9e58..42e604e9a6 100644
--- a/src/test/groovy/graphql/parser/ParserTest.groovy
+++ b/src/test/groovy/graphql/parser/ParserTest.groovy
@@ -4,6 +4,7 @@ package graphql.parser
 import graphql.language.Argument
 import graphql.language.ArrayValue
 import graphql.language.AstComparator
+import graphql.language.AstPrinter
 import graphql.language.BooleanValue
 import graphql.language.Description
 import graphql.language.Directive
@@ -1151,5 +1152,40 @@ triple3 : """edge cases \\""" "" " \\"" \\" edge cases"""
         document.getDefinitions()[0].getSourceLocation() == SourceLocation.EMPTY
     }
 
+    def "escape characters correctly printed when printing AST"() {
+        given:
+        def env = newParserEnvironment()
+                .document(src)
+                .parserOptions(
+                        ParserOptions.newParserOptions()
+                                .captureIgnoredChars(true)
+                                .build()
+                )
+                .build()
+
+        when:
+        // Parse the original Document
+        def doc = Parser.parse(env)
+        // Print the AST
+        def printed = AstPrinter.printAst(doc)
+        // Re-parse printed AST
+        def reparsed = Parser.parse(printed)
+
+        then:
+        noExceptionThrown() // The printed AST was re-parsed without exception
+
+        when:
+        def reparsedPrinted = AstPrinter.printAst(reparsed)
+
+        then:
+        reparsedPrinted == printed // Re-parsing and re-printing produces the same result
+
+        where:
+        src                 | _
+        "\"\\\"\" scalar A" | _
+        "\"\f\" scalar A"   | _
+        "\"\b\" scalar A"   | _
+        "\"\t\" scalar A"   | _
+    }
 
 }