{"record":{"id":"a68247c1b331a164","repo":"alibaba/spring-ai-alibaba","slug":"status-value-cannot-be-null","errorCode":null,"errorMessage":"Status value cannot be null","messagePattern":"Status value cannot be null","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/todolist/TodoListInterceptor.java","lineNumber":140,"sourceCode":"\t/**\n\t * Todo item status.\n\t */\n\t@JsonFormat(shape = JsonFormat.Shape.STRING)\n\tpublic enum TodoStatus {\n\t\tPENDING(\"pending\"),\n\t\tIN_PROGRESS(\"in_progress\"),\n\t\tCOMPLETED(\"completed\");\n\n\t\tprivate final String value;\n\n\t\tTodoStatus(String value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@JsonCreator\n\t\tpublic static TodoStatus fromValue(String value) {\n\t\t\tif (value == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"Status value cannot be null\");\n\t\t\t}\n\n\t\t\t// First try to match against the lowercase values\n\t\t\tfor (TodoStatus status : values()) {\n\t\t\t\tif (status.value.equals(value)) {\n\t\t\t\t\treturn status;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fallback: try to match against enum constant names (case-insensitive)\n\t\t\ttry {\n\t\t\t\treturn TodoStatus.valueOf(value.toUpperCase());\n\t\t\t}\n\t\t\tcatch (IllegalArgumentException e) {\n\t\t\t\t// If that fails too, throw a helpful error\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Unknown status: \" + value + \". Valid values are: pending, in_progress, completed\");\n\t\t\t}","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/todolist/TodoListInterceptor.java#L122-L158","documentation":"TodoListInterceptor.TodoStatus.fromValue() is the Jackson @JsonCreator for deserializing todo status strings. A null input cannot match any status, so it throws IllegalArgumentException before the value-matching loop runs.","triggerScenarios":"Deserializing a todo JSON whose \"status\" field is null or absent and fed explicitly as null into fromValue(); programmatic calls like TodoStatus.fromValue(null).","commonSituations":"LLM-produced todo JSON missing the status field; API payload with explicit null; calling the creator directly from tests or custom deserializers.","solutions":["Ensure todo items always carry a non-null status string before serialization/deserialization","Default null to TodoStatus.PENDING in the owning code","Wrap the parse in a null check and handle/replace malformed items"],"exampleFix":"// before\nTodoStatus status = TodoStatus.fromValue(item.get(\"status\")); // may be null\n// after\nString v = item.get(\"status\");\nTodoStatus status = v != null ? TodoStatus.fromValue(v) : TodoStatus.PENDING;","handlingStrategy":"type-guard","validationCode":"String v = todo.get(\"status\");\nif (v == null || v.isBlank()) { v = \"pending\"; }\nTodoStatus status = TodoStatus.fromValue(v);","typeGuard":"boolean hasStatus(JsonNode n) { return n.has(\"status\") && !n.get(\"status\").isNull(); }","tryCatchPattern":"try { status = TodoStatus.fromValue(value); } catch (IllegalArgumentException e) { status = TodoStatus.PENDING; }","preventionTips":["Always emit an explicit status field in todo JSON","Default missing statuses to pending","Prompt the LLM with the allowed status values"],"tags":["jackson","null","deserialization","todolist"],"backgroundTag":"null-argument","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}