{"record":{"id":"d91f82321fb5e7ce","repo":"alibaba/nacos","slug":"runtime-endpoint-bindings-must-not-be-empty","errorCode":null,"errorMessage":"Runtime Endpoint bindings must not be empty","messagePattern":"Runtime Endpoint bindings must not be empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/com/alibaba/nacos/ai/service/agent/fingerprint/RuntimeEndpointRevision.java","lineNumber":132,"sourceCode":"                writeUtf8(output, endpoint.getUri());\n                writeUtf8(output, endpoint.getTransport());\n                output.writeInt(endpoint.getPriority());\n                double weight = endpoint.getWeight() == 0D ? 0D : endpoint.getWeight();\n                output.writeLong(Double.doubleToLongBits(weight));\n                writeMetadata(output, endpoint.getMetadata());\n                output.writeBoolean(endpoint.getHealthy());\n                writeBindings(output, endpoint.getBindings());\n            }\n        } catch (IOException e) {\n            throw new IllegalStateException(\"Unable to frame Runtime Endpoint projection\", e);\n        }\n        return buffer.toByteArray();\n    }\n    \n    private static List<RuntimeVersionBinding> canonicalBindings(\n        List<RuntimeVersionBinding> bindings) {\n        if (bindings == null || bindings.isEmpty()) {\n            throw new IllegalArgumentException(\"Runtime Endpoint bindings must not be empty\");\n        }\n        List<RuntimeVersionBinding> result = new ArrayList<RuntimeVersionBinding>(bindings);\n        for (RuntimeVersionBinding binding : result) {\n            if (binding == null || binding.getRuntimeVersion() == null\n                || binding.getVersionRange() == null) {\n                throw new IllegalArgumentException(\"Runtime Endpoint binding must be complete\");\n            }\n        }\n        Collections.sort(result, new Comparator<RuntimeVersionBinding>() {\n            \n            @Override\n            public int compare(RuntimeVersionBinding left, RuntimeVersionBinding right) {\n                int comparison = AgentVersionComparator.compare(left.getRuntimeVersion(),\n                    right.getRuntimeVersion());\n                return comparison == 0\n                    ? left.getVersionRange().compareTo(right.getVersionRange()) : comparison;\n            }\n        });","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/fingerprint/RuntimeEndpointRevision.java#L114-L150","documentation":"Every runtime Endpoint in a revision projection must carry a non-empty list of RuntimeVersionBindings. This guard in canonicalBindings runs during revision computation, ensuring each endpoint declares which runtime implementation versions and Agent version ranges it can serve.","triggerScenarios":"Calling RuntimeEndpointRevision.compute with an endpoints list where at least one AgentDiscoveryEndpoint has getBindings() returning null or an empty list. The canonicalBindings method at line 129 is invoked for each endpoint during canonicalization.","commonSituations":"A runtime agent registers endpoints without specifying version bindings. An adaptor maps endpoints but omits the bindings field. JSON deserialization leaves bindings unset because the field was absent in the registration payload.","solutions":["Set endpoint.setBindings(...) with at least one RuntimeVersionBinding before computing the revision.","Ensure each runtime endpoint declares its runtimeVersion and the Agent versionRange it serves.","Default to a single binding covering all versions if the runtime serves a broad range."],"exampleFix":"// before\nendpoint.setBindings(null);\n// or\nendpoint.setBindings(Collections.emptyList());\nRuntimeEndpointRevision.compute(ns, name, proto, endpoints); // throws\n\n// after\nRuntimeVersionBinding binding = new RuntimeVersionBinding();\nbinding.setRuntimeVersion(\"1.0.0\");\nbinding.setVersionRange(\"[1.0,2.0)\");\nendpoint.setBindings(List.of(binding));\nRuntimeEndpointRevision.compute(ns, name, proto, endpoints);","handlingStrategy":"validation","validationCode":"for (AgentDiscoveryEndpoint ep : endpoints) {\n    if (ep.getBindings() == null || ep.getBindings().isEmpty()) {\n        throw new IllegalArgumentException(\"Endpoint bindings empty: \" + ep.getUri());\n    }\n}","typeGuard":"boolean allEndpointsHaveBindings(List<AgentDiscoveryEndpoint> endpoints) {\n    return endpoints.stream().allMatch(e -> e.getBindings() != null && !e.getBindings().isEmpty());\n}","tryCatchPattern":null,"preventionTips":["Always populate bindings with at least one RuntimeVersionBinding per endpoint.","Ensure the registration payload includes version bindings for each endpoint.","Validate bindings are non-empty client-side before submission."],"tags":["agent","ai-registry","rad-protocol","runtime-endpoint","version-binding"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}