{"record":{"id":"3f2ee6b405c2f56d","repo":"json-path/JsonPath","slug":"unexpected-character-c","errorCode":null,"errorMessage":"Unexpected character: %c","messagePattern":"Unexpected character: %c","errorType":"exception","errorClass":"InvalidPathException","httpStatus":null,"severity":"error","filePath":"json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java","lineNumber":102,"sourceCode":"\n             return result;\n        } catch (InvalidPathException e){\n            throw e;\n        } catch (Exception e) {\n            throw new InvalidPathException(\"Failed to parse filter: \" + filter + \", error on position: \" + filter.position() + \", char: \" + filter.currentChar());\n        }\n    }\n\n    private ValueNode readValueNode() {\n        switch (filter.skipBlanks().currentChar()) {\n            case DOC_CONTEXT  : return readPath();\n            case EVAL_CONTEXT : return readPath();\n            case NOT:\n                filter.incrementPosition(1);\n                switch (filter.skipBlanks().currentChar()) {\n                    case DOC_CONTEXT  : return readPath();\n                    case EVAL_CONTEXT : return readPath();\n                    default: throw new InvalidPathException(String.format(\"Unexpected character: %c\", NOT));\n                }\n            default : return readLiteral();\n        }\n    }\n\n    private ValueNode readLiteral(){\n        switch (filter.skipBlanks().currentChar()){\n            case SINGLE_QUOTE:  return readStringLiteral(SINGLE_QUOTE);\n            case DOUBLE_QUOTE: return readStringLiteral(DOUBLE_QUOTE);\n            case TRUE:  return readBooleanLiteral();\n            case FALSE: return readBooleanLiteral();\n            case MINUS: return readNumberLiteral();\n            case NULL:  return readNullLiteral();\n            case OPEN_OBJECT: return readJsonLiteral();\n            case OPEN_ARRAY: return readJsonLiteral();\n            case PATTERN: return readPattern();\n            default:    return readNumberLiteral();\n        }","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/json-path/JsonPath/blob/62a4c9f0f65ba3f625aa0867d64c528ba72d09ec/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java#L84-L120","documentation":"This InvalidPathException is thrown by JsonPath's FilterCompiler while parsing an inline filter predicate. Inside readValueNode, when the current token is the NOT operator ('!'), the compiler expects a path starting with the document context char '@' or the evaluation context char '#' to follow; any other character (e.g. '!x' or '! 'a' == 'b') is unrecoverable. It means the negation operator in the filter is not immediately followed by a valid path operand.","triggerScenarios":"Compiling a filter path where '!' is followed by something that is not '@' or '#', e.g. \"$[?(! foo)]\", \"$[?(! 'a' == 'b')]\", or \"$[?(!)]\" — anywhere Filter.compile / JsonPath.parse(...).read(path) evaluates the predicate.","commonSituations":"Hand-written filter expressions with a stray space or wrong operand after '!' (users expect '!= value' syntax to be written as '! ='); macro-generated paths that prepend '!' to a literal instead of a path; upgrading JsonPath and an expression that previously slipped through stricter validation now fails.","solutions":["Fix the filter so '!' is directly followed by a path operand: use \"$[?(!@.field)]\" or \"$[?(!(@.field == 'x'))]\"","If you meant inequality, use the '!=' operator instead of '!': \"$[?(@.field != 'x')]\"","Wrap negation in parentheses around a comparison: \"$[?(!(@.a && @.b))]\" rather than negating a literal","Verify the whole expression compiles with Filter.compile(predicate) in a unit test before deploying"],"exampleFix":"// before\nString path = \"$[?(! 'active' == true)]\";\n// after\nString path = \"$[?(!@.active == true)]\";","handlingStrategy":"validation","validationCode":"// Validate a negation before compiling\nboolean validNegation(String predicate) {\n    int i = predicate.indexOf('!');\n    if (i < 0 || i == predicate.length() - 1) return false;\n    char next = predicate.charAt(i + 1);\n    return next == '@' || next == '#' || next == '(';\n}\n// usage: if (!validNegation(\"$[?(!@.x)]\")) throw new IllegalArgumentException(\"bad filter\");","typeGuard":"static boolean isNegatingPath(String filter) {\n    java.util.regex.Matcher m = java.util.regex.Pattern.compile(\"!\\\\s*[@#(]\").matcher(filter);\n    return m.find();\n}","tryCatchPattern":"try {\n    Object result = JsonPath.parse(json).read(path);\n} catch (InvalidPathException e) {\n    log.error(\"Malformed filter predicate, ! must precede @ or # : \" + path, e);\n    throw new IllegalArgumentException(\"Invalid JSONPath filter: \" + path, e);\n}","preventionTips":["Always follow '!' immediately with @, #, or a parenthesized group","Use '!=' for inequality instead of '! ='","Unit-test every user-supplied filter string with Filter.compile() before use"],"tags":["json-path","filter-compiler","path-syntax","invalid-path-exception"],"backgroundTag":"invalid-path-expression","analyzedSha":"62a4c9f0f65ba3f625aa0867d64c528ba72d09ec","analyzedAt":"2026-09-11T11:36:00.448Z","contentChangedAt":"2026-09-11T11:36:00.448Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}