apache/iceberg · error · IllegalArgumentException

Malformed JSON

Error message

Malformed JSON: %s

What it means

SnowflakeTableMetadata.parseJson wraps IOExceptions from Jackson when parsing the metadata JSON string returned by the Snowflake client into IllegalArgumentException with 'Malformed JSON'. This payload comes from Snowflake's GET (system) GET_ICEBERG_TABLE_INFO/metadata API, so a non-JSON response indicates a client/protocol problem rather than bad user data.

Solutions

  1. Verify Snowflake connectivity and credentials; an error page or empty body usually means auth/session failure upstream of this parse.
  2. Upgrade the Snowflake JDBC driver and iceberg-snowflake module to matched recent versions so the metadata payload shape is supported.
  3. Log the raw JSON string from the exception message and inspect it — it reveals what the client actually received (HTML vs truncated JSON).
  4. Retry the load; transient Snowflake API failures can produce malformed bodies. Add retry with backoff around loadTableMetadata.

Example fix

// before
SnowflakeTableMetadata meta = SnowflakeTableMetadata.parseJson(client.loadTableMetadata(id));
// after
String json = client.loadTableMetadata(id);
if (json == null || !json.trim().startsWith("{")) {
  throw new IllegalStateException("Unexpected Snowflake metadata payload: " + json);
}
SnowflakeTableMetadata meta = SnowflakeTableMetadata.parseJson(json);
Defensive patterns

Strategy: try-catch

Validate before calling

if (json == null || json.trim().isEmpty() || !(json.trim().startsWith("{") || json.trim().startsWith("["))) {
  throw new IllegalStateException("Snowflake metadata payload is not JSON: " + json);
}

Try / catch

try { meta = SnowflakeTableMetadata.parseJson(json); } catch (IllegalArgumentException e) { LOG.error("Bad Snowflake metadata payload: {}", json, e); throw new RetryableLoadException(e); }

Prevention

When it happens

Trigger: snowflakeClient.loadTableMetadata returns a string that is not valid JSON (empty string, HTML error page, truncated response, driver-level error text), which readValue fails to parse.

Common situations: Expired or misconfigured Snowflake session returning an error body instead of JSON; older Snowflake JDBC/driver versions returning a different payload shape; network proxies or Snowflake outages injecting non-JSON error pages; programmatic misuse of parseJson with hand-built strings.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/e3968902d5de3c57. Report an issue: GitHub.

Appendix: source

Thrown at snowflake/src/main/java/org/apache/iceberg/snowflake/SnowflakeTableMetadata.java:139

      // gcs://bucket/path
      // to:
      // gs://bucket/path
      return "gs" + snowflakeLocation.substring(3);
    }

    return snowflakeLocation;
  }

  /**
   * Factory method for parsing a JSON string containing expected Snowflake table metadata into a
   * SnowflakeTableMetadata object.
   */
  public static SnowflakeTableMetadata parseJson(String json) {
    JsonNode parsedVal;
    try {
      parsedVal = JsonUtil.mapper().readValue(json, JsonNode.class);
    } catch (IOException ioe) {
      throw new IllegalArgumentException(String.format("Malformed JSON: %s", json), ioe);
    }

    String snowflakeMetadataLocation = JsonUtil.getString("metadataLocation", parsedVal);
    String status = JsonUtil.getStringOrNull("status", parsedVal);

    String icebergMetadataLocation = snowflakeLocationToIcebergLocation(snowflakeMetadataLocation);

    return new SnowflakeTableMetadata(
        snowflakeMetadataLocation, icebergMetadataLocation, status, json);
  }
}

View on GitHub (pinned to 86d9c8fc54)