{"record":{"id":"08ded05244932c8d","repo":"hibernate/hibernate-orm","slug":"unexpected-processing-state","errorCode":null,"errorMessage":"unexpected processing state: {}","messagePattern":"unexpected processing state: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java","lineNumber":381,"sourceCode":"\t\tif (endingQuote == -1) {\n\t\t\tthrow new IllegalStateException(\"Can't find ending quote of key name\");\n\t\t}\n\n\t\tthis.jsonValueEnd = endingQuote;\n\t\tthis.jsonValueStart = position;\n\n\t\tthis.position =  endingQuote + 1;\n\n\t}\n\n\t/**\n\t * Ensures that the current state is on value.\n\t * @throws IllegalStateException if not on \"value\" state\n\t */\n\tprivate void ensureValueState() throws IllegalStateException {\n\t\tif ( (this.processingStates.getCurrent() != JsonProcessingState.OBJECT ) &&\n\t\t\tthis.processingStates.getCurrent() != JsonProcessingState.ARRAY)  {\n\t\t\tthrow new IllegalStateException( \"unexpected processing state: \" + this.processingStates.getCurrent() );\n\t\t}\n\t}\n\t/**\n\t * Ensures that we have a value ready to be exposed. i.e we just consume one.\n\t * @throws IllegalStateException if no value available\n\t */\n\tprivate void ensureAvailableValue() throws IllegalStateException {\n\t\tif (this.jsonValueEnd == 0 ) {\n\t\t\tthrow new IllegalStateException( \"No available value\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getObjectKeyName() {\n\t\tif ( this.processingStates.getCurrent() != JsonProcessingState.OBJECT_KEY_NAME ) {\n\t\t\tthrow new IllegalStateException( \"unexpected processing state: \" + this.processingStates.getCurrent() );\n\t\t}\n\t\tensureAvailableValue();","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java#L363-L399","documentation":"Every typed value accessor on JsonDocumentReader (getStringValue, getIntegerValue, getBigDecimalValue, getBooleanValue, getDoubleValue, ...) first calls ensureValueState(), which requires the parser to be at an object attribute value position (state OBJECT) or inside an array (state ARRAY). Calling any typed getter in another state throws this IllegalStateException with the actual state in the message.","triggerScenarios":"Calling getStringValue()/getIntegerValue()/... immediately after next() returned VALUE_KEY (state OBJECT_KEY_NAME), before any object/array was entered (state NONE/STARTING_OBJECT), or at any point where the parser sits on a key rather than a value.","commonSituations":"Custom JSON/XML format mappers, aggregate mapping code, or traversal utilities that call a typed getter at the wrong step of the pull protocol; code ported from a push-style API where characters arrive in callbacks; copy-pasted loops assuming the first next() already yields a value.","solutions":["Drive all accessor calls from the JsonDocumentItemType returned by next(): only call typed getters after VALUE, NUMERIC_VALUE, BOOLEAN_VALUE or NULL_VALUE items","Call getObjectKeyName() only after VALUE_KEY, and match the getter to the item type (NUMERIC_VALUE → getIntegerValue/getLongValue/getBigDecimalValue, BOOLEAN_VALUE → getBooleanValue)","Encapsulate the protocol once in a small traversal wrapper so call sites cannot get it wrong"],"exampleFix":"// before\nJsonDocumentItemType item = reader.next(); // returned VALUE_KEY\nString s = reader.getStringValue(); // IllegalStateException: state is OBJECT_KEY_NAME\n// after\nswitch (reader.next()) {\n    case VALUE_KEY:\n        String key = reader.getObjectKeyName();\n        break;\n    case VALUE:\n    case NUMERIC_VALUE:\n    case BOOLEAN_VALUE:\n    case NULL_VALUE:\n        String s2 = reader.getStringValue();\n        break;\n}","handlingStrategy":"validation","validationCode":"static boolean isAtValue(JsonDocumentItemType lastItem) {\n    return lastItem == JsonDocumentItemType.VALUE\n            || lastItem == JsonDocumentItemType.NUMERIC_VALUE\n            || lastItem == JsonDocumentItemType.BOOLEAN_VALUE\n            || lastItem == JsonDocumentItemType.NULL_VALUE;\n}\n\n// usage: track the item returned by next() and only call typed getters when isAtValue(lastItem) is true","typeGuard":null,"tryCatchPattern":"try {\n    value = reader.getStringValue();\n} catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"unexpected processing state\")) {\n        // protocol violation: a value item was not consumed before the getter\n        skipToNextValue(reader);\n        return null;\n    }\n    throw e;\n}","preventionTips":["Model the pull protocol explicitly: one switch over JsonDocumentItemType drives every accessor call","Typed getters only after VALUE/NUMERIC_VALUE/BOOLEAN_VALUE/NULL_VALUE; getObjectKeyName only after VALUE_KEY","Write one shared traversal helper instead of scattering direct reader calls"],"tags":["hibernate","json","parser","api-misuse","state-machine"],"backgroundTag":"invalid-parser-state","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}