{"record":{"id":"947936ac07a31a5f","repo":"grpc/grpc-java","slug":"unable-to-parse-custom-lb-config-json","errorCode":null,"errorMessage":"Unable to parse custom LB config JSON","messagePattern":"Unable to parse custom LB config JSON","errorType":"validation","errorClass":"ResourceInvalidException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/LoadBalancerConfigFactory.java","lineNumber":371,"sourceCode":"     */\n    @SuppressWarnings(\"unchecked\")\n    private static ImmutableMap<String, ?> convertCustomConfig(\n        com.github.udpa.udpa.type.v1.TypedStruct configTypedStruct)\n        throws ResourceInvalidException {\n      return ImmutableMap.of(parseCustomConfigTypeName(configTypedStruct.getTypeUrl()),\n          (Map<String, ?>) parseCustomConfigJson(configTypedStruct.getValue()));\n    }\n\n    /**\n     * Print the config Struct into JSON and then parse that into our internal representation.\n     */\n    private static Object parseCustomConfigJson(Struct configStruct)\n        throws ResourceInvalidException {\n      Object rawJsonConfig = null;\n      try {\n        rawJsonConfig = JsonParser.parse(JsonFormat.printer().print(configStruct));\n      } catch (IOException e) {\n        throw new ResourceInvalidException(\"Unable to parse custom LB config JSON\", e);\n      }\n\n      if (!(rawJsonConfig instanceof Map)) {\n        throw new ResourceInvalidException(\"Custom LB config does not contain a JSON object\");\n      }\n      return rawJsonConfig;\n    }\n\n\n    private static String parseCustomConfigTypeName(String customConfigTypeName) {\n      if (customConfigTypeName.contains(\"/\")) {\n        customConfigTypeName = customConfigTypeName.substring(\n            customConfigTypeName.lastIndexOf(\"/\") + 1);\n      }\n      return customConfigTypeName;\n    }\n\n    // Used to signal that the LB config goes too deep.","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/LoadBalancerConfigFactory.java#L353-L389","documentation":"Custom (third-party) LB policy configs arrive in the xDS proto as a google.protobuf.Struct. The factory prints the Struct back to JSON and parses it with grpc's JsonParser; if printing/parsing throws an IOException, the config cannot be turned into the JSON object form the LB provider expects, so a ResourceInvalidException is thrown.","triggerScenarios":"convertCustomConfig calls parseCustomConfigJson with a Struct that JsonFormat.printer().print() cannot serialize or JsonParser.parse() cannot parse — typically a Struct containing values incompatible with the JSON printer (e.g. invalid protobuf value types).","commonSituations":"A control plane sending a malformed custom LB policy Struct; proto library version mismatch where the Struct contains value types the printer rejects; corrupted ADS response.","solutions":["Inspect the raw xDS response and fix the malformed Struct on the management server","Verify protobuf/Envoy API dependency versions are consistent (JsonFormat printer behavior differs across versions)","Simplify the custom config to plain JSON-compatible fields and retest","Check grpc xDS debug logs for the wrapped IOException cause for the exact offending value"],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// Sanity-check the custom config is printable JSON before sending:\nJsonFormat.printer().printingEscapeNonAscii(true).print(struct); // throws if malformed","typeGuard":null,"tryCatchPattern":"try {\n  Object cfg = parseCustomConfigJson(struct);\n} catch (ResourceInvalidException e) {\n  logger.warning(\"Custom LB config rejected: \" + e.getMessage());\n  // treat resource as invalid, fall back to a known-good LB policy\n}","preventionTips":["Keep custom LB configs as plain JSON-compatible Struct fields","Avoid mixed proto/JSON tooling versions on the control plane","Log raw ADS responses to catch malformed Structs early"],"tags":["grpc","xds","json","custom-lb-policy"],"backgroundTag":"json-parse-error","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}