{"record":{"id":"eeca7171fac6df47","repo":"apache/iceberg","slug":"failed-to-parse-streamingoffset-from-json-string-eeca71","errorCode":null,"errorMessage":"Failed to parse StreamingOffset from JSON string %s","messagePattern":"Failed to parse StreamingOffset from JSON string (.+?)","errorType":"exception","errorClass":"UncheckedIOException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/source/StreamingOffset.java","lineNumber":68,"sourceCode":"   * @param snapshotId The current processed snapshot id.\n   * @param position The position of last scanned file in snapshot.\n   * @param scanAllFiles whether to scan all files in a snapshot; for example, to read all data when\n   *     starting a stream.\n   */\n  StreamingOffset(long snapshotId, long position, boolean scanAllFiles) {\n    this.snapshotId = snapshotId;\n    this.position = position;\n    this.scanAllFiles = scanAllFiles;\n  }\n\n  static StreamingOffset fromJson(String json) {\n    Preconditions.checkNotNull(json, \"Cannot parse StreamingOffset JSON: null\");\n\n    try {\n      JsonNode node = JsonUtil.mapper().readValue(json, JsonNode.class);\n      return fromJsonNode(node);\n    } catch (IOException e) {\n      throw new UncheckedIOException(\n          String.format(\"Failed to parse StreamingOffset from JSON string %s\", json), e);\n    }\n  }\n\n  static StreamingOffset fromJson(InputStream inputStream) {\n    Preconditions.checkNotNull(inputStream, \"Cannot parse StreamingOffset from inputStream: null\");\n\n    JsonNode node;\n    try {\n      node = JsonUtil.mapper().readValue(inputStream, JsonNode.class);\n    } catch (IOException e) {\n      throw new UncheckedIOException(\"Failed to read StreamingOffset from json\", e);\n    }\n\n    return fromJsonNode(node);\n  }\n\n  @Override","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/source/StreamingOffset.java#L50-L86","documentation":"StreamingOffset.fromJson parses a JSON string into a StreamingOffset (used as a Spark micro-batch offset). If the string is not valid JSON or does not match the expected structure, the IOException (or structure failure) is wrapped in UncheckedIOException with the offending string in the message.","triggerScenarios":"Passing a malformed, truncated, or non-Iceberg JSON string to StreamingOffset.fromJson, e.g. a corrupted checkpoint offset value from the Spark streaming source.","commonSituations":"Corrupted or manually edited Spark streaming checkpoint directories; restoring checkpoints from a different Iceberg version whose offset schema differs; passing user-supplied strings during offset deserialization in tests.","solutions":["Inspect the printed JSON string in the message and correct/repair the checkpoint offset file","Restore the streaming query from a fresh checkpoint if the offset is unrecoverable","Verify both producer and consumer use the same Iceberg version so the offset schema matches","When parsing programmatically, validate the JSON structure before calling fromJson"],"exampleFix":"// before\nStreamingOffset offset = StreamingOffset.fromJson(corruptedOffsetJson);\n// after\nStreamingOffset offset = StreamingOffset.fromJson(\n    JsonUtil.mapper().writeValueAsString(\n        Map.of(\"version\", 1, \"snapshot_id\", snapshotId, \"position\", 0, \"scan_all_files\", false)));","handlingStrategy":"try-catch","validationCode":"if (json == null || json.isBlank() || !json.trim().startsWith(\"{\")) {\n  throw new IllegalArgumentException(\"Not a valid StreamingOffset JSON: \" + json);\n}","typeGuard":null,"tryCatchPattern":"try {\n  StreamingOffset offset = StreamingOffset.fromJson(json);\n} catch (UncheckedIOException e) {\n  // fall back to START_OFFSET or fail the batch with a checkpoint-repair path\n  StreamingOffset offset = StreamingOffset.START_OFFSET;\n}","preventionTips":["Never hand-edit Spark checkpoint offset files","Keep the Iceberg version identical between jobs sharing a checkpoint","Round-trip offsets through toJson/fromJson in tests to catch schema drift"],"tags":["json-parse-error","spark","streaming","checkpoint"],"backgroundTag":"json-parse-error","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}