{"record":{"id":"ee657f2b1c3218a8","repo":"apache/incubator-seata","slug":"metadata-external-is-not-a-list","errorCode":null,"errorMessage":"Metadata 'external' is not a List.","messagePattern":"Metadata 'external' is not a List\\.","errorType":"validation","errorClass":"ParseEndpointException","httpStatus":null,"severity":"error","filePath":"common/src/main/java/org/apache/seata/common/metadata/Node.java","lineNumber":242,"sourceCode":"    }\n\n    public Map<String, Object> updateMetadataWithExternalEndpoints(\n            Map<String, Object> metadata, List<Node.ExternalEndpoint> externalEndpoints) {\n        Object obj = metadata.get(\"external\");\n        if (obj == null) {\n            if (!externalEndpoints.isEmpty()) {\n                Map<String, Object> metadataMap = new HashMap<>(metadata);\n                metadataMap.put(\"external\", externalEndpoints);\n                return metadataMap;\n            }\n            return metadata;\n        }\n        if (obj instanceof List) {\n            List<Node.ExternalEndpoint> oldList = (List<Node.ExternalEndpoint>) obj;\n            oldList.addAll(externalEndpoints);\n            return metadata;\n        } else {\n            throw new ParseEndpointException(\"Metadata 'external' is not a List.\");\n        }\n    }\n\n    public static class ExternalEndpoint {\n\n        private String host;\n        private int controlPort;\n        private int transactionPort;\n\n        public ExternalEndpoint(String host, int controlPort, int transactionPort) {\n            this.host = host;\n            this.controlPort = controlPort;\n            this.transactionPort = transactionPort;\n        }\n\n        public String getHost() {\n            return host;\n        }","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/apache/incubator-seata/blob/e01f97c6db397165050caa6764020410c2c8199a/common/src/main/java/org/apache/seata/common/metadata/Node.java#L224-L260","documentation":"Node.updateMetadataWithExternalEndpoints expects metadata['external'] to be either absent or a List. If the key exists but holds another type (String, Map, scalar), it throws ParseEndpointException 'Metadata 'external' is not a List.' because it needs to append ExternalEndpoint objects to the existing list.","triggerScenarios":"Calling updateMetadataWithExternalEndpoints with a metadata map where 'external' was previously populated as a non-List value — typically another component serialized it to a JSON string, or a custom metadata provider set it to a Map/object.","commonSituations":"Mixing seata versions where 'external' metadata shape changed; custom metadata providers or interceptors writing a string into the 'external' key; deserializing metadata from JSON into Map<String,Object> that yields a String instead of List.","solutions":["Find who wrote the non-List value into metadata['external'] and have it write a List<Node.ExternalEndpoint> (or omit the key).","If metadata came from JSON deserialization, ensure 'external' decodes as an array, not an object/string.","Upgrade both seata server and client to versions agreeing on the 'external' metadata shape."],"exampleFix":"// before\nmetadata.put(\"external\", \"10.0.0.1:7091:8091\"); // String -> throws on update\n\n// after\nmetadata.put(\"external\", new ArrayList<Node.ExternalEndpoint>(\n    List.of(new Node.ExternalEndpoint(\"10.0.0.1\", 7091, 8091))));","handlingStrategy":"type-guard","validationCode":"Map<String, Object> safe = new HashMap<>(metadata);\nObject ext = safe.get(\"external\");\nif (ext != null && !(ext instanceof List)) {\n    safe.remove(\"external\"); // or convert: safe.put(\"external\", parseToList(ext));\n}\nnode.updateMetadataWithExternalEndpoints(safe, externalEndpoints);","typeGuard":"boolean hasListExternal(Map<String, Object> metadata) {\n    Object ext = metadata == null ? null : metadata.get(\"external\");\n    return ext == null || ext instanceof List;\n}","tryCatchPattern":"try {\n    node.updateMetadataWithExternalEndpoints(metadata, externalEndpoints);\n} catch (ParseEndpointException e) {\n    if (e.getMessage().contains(\"not a List\")) {\n        // metadata['external'] corrupted by another writer: reset the key to a fresh List and retry\n    }\n    throw e;\n}","preventionTips":["Only ever write List<Node.ExternalEndpoint> into the 'external' metadata key.","When deserializing cluster metadata from JSON, assert 'external' decodes as an array.","Keep seata server and client versions aligned so metadata shapes agree."],"tags":["metadata","endpoint","type-mismatch","seata"],"backgroundTag":null,"analyzedSha":"e01f97c6db397165050caa6764020410c2c8199a","analyzedAt":"2026-08-14T10:23:53.097Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}