{"id":"0374c81cddb2fe5e","repo":"google/gson","slug":"missing-id-or-totalseconds-field-at-path-path","errorCode":null,"errorMessage":"Missing id or totalSeconds field; at path {path}","messagePattern":"Missing id or totalSeconds field; at path (.+?)","errorType":"exception","errorClass":"JsonSyntaxException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/JavaTimeTypeAdapters.java","lineNumber":319,"sourceCode":"            switch (name) {\n              case \"id\":\n                id = in.nextString();\n                break;\n              case \"totalSeconds\":\n                totalSeconds = in.nextInt();\n                break;\n              default:\n                // Ignore other fields.\n                in.skipValue();\n            }\n          }\n          in.endObject();\n          if (id != null) {\n            return ZoneId.of(id);\n          } else if (totalSeconds != null) {\n            return ZoneOffset.ofTotalSeconds(totalSeconds);\n          } else {\n            throw new JsonSyntaxException(\n                \"Missing id or totalSeconds field; at path \" + in.getPreviousPath());\n          }\n        }\n\n        @Override\n        public void write(JsonWriter out, ZoneId value) throws IOException {\n          if (value instanceof ZoneOffset) {\n            out.beginObject();\n            out.name(\"totalSeconds\");\n            out.value(((ZoneOffset) value).getTotalSeconds());\n            out.endObject();\n          } else {\n            out.beginObject();\n            out.name(\"id\");\n            out.value(value.getId());\n            out.endObject();\n          }\n        }","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/JavaTimeTypeAdapters.java#L301-L337","documentation":"Thrown by Gson's built-in ZoneId adapter when reading a JSON object that contains neither an 'id' field (for a region-based ZoneId like 'America/New_York') nor a 'totalSeconds' field (for a ZoneOffset). This representation mimics what reflective serialization of java.time.ZoneId produced historically, so it requires one of those two discriminators. The {path} is the reader's previous path.","triggerScenarios":"Deserializing a ZoneId or ZoneOffset field whose JSON object omits both keys, e.g. '{}' or '{\"foo\":\"bar\"}'. Common when the producer uses a different ZoneId encoding (a bare string like \"Z\" or \"+02:00\", or a nested object with different keys) than Gson's reflective integer-fields representation.","commonSituations":"Interoperating with systems that serialize ZoneId as a plain ISO string; upgrading from older Gson that used reflection and now using the explicit java.time adapter; partial JSON from a trimmed log.","solutions":["Ensure the source emits either {\"id\":\"<zoneId>\"} or {\"totalSeconds\":<int>} for ZoneId/ZoneOffset fields.","Register a custom TypeAdapter<ZoneId> that accepts the producer's actual encoding (e.g. a bare string via ZoneId.of(s)).","If migrating from reflection-based output, regenerate the data with the current Gson version so the keys match.","Pre-validate the JSON object has the required key before deserializing, and surface a clearer error to the data producer."],"exampleFix":"// before: producer sends {\"zone\":\"Z\"}  -> JsonSyntaxException\n// after: register a custom ZoneId adapter that reads the string form\nGson gson = new GsonBuilder()\n    .registerTypeAdapter(ZoneId.class, new TypeAdapter<ZoneId>() {\n        @Override public ZoneId read(JsonReader in) throws IOException {\n            return ZoneId.of(in.nextString());\n        }\n        @Override public void write(JsonWriter out, ZoneId v) throws IOException {\n            out.value(v.getId());\n        }\n    }.nullSafe())\n    .create();","handlingStrategy":"validation","validationCode":"// Pre-check JSON has one of the two supported keys for ZoneId\nJsonObject o = JsonParser.parseString(json).getAsJsonObject();\nif (!o.has(\"id\") && !o.has(\"totalSeconds\")) {\n  throw new IllegalArgumentException(\"ZoneId JSON must have 'id' or 'totalSeconds'\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  return gson.fromJson(json, ZoneId.class);\n} catch (JsonSyntaxException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Missing id or totalSeconds\")) {\n    // fall back to a string-based ZoneId interpretation\n    return ZoneId.of(JsonParser.parseString(json).getAsString());\n  }\n  throw e;\n}","preventionTips":["Standardize on one ZoneId encoding across producer and consumer.","Register a custom ZoneId adapter matching the producer's actual format.","Add contract tests asserting the JSON keys for java.time fields."],"tags":["java-time","zoneid","deserialization","interoperability"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}