testcontainers/testcontainers-java · error · IllegalStateException
'/clusters/0/cluster' expected to be an object
Error message
'/clusters/0/cluster' expected to be an object
What it means
When rewriting the server URL inside the bundled kubeconfig YAML, K3sContainer parses it with Jackson and expects the path /clusters/0/cluster to be a JSON/YAML object. If the YAML shape changed (parse produced a non-object node, e.g. missing, null, or a different structure), the method aborts with IllegalStateException because it cannot safely replace the 'server' field.
Solutions
- Restore/keep the default kubeconfig template shipped with testcontainers-k3s so clusters[0].cluster is a mapping
- If customizing the YAML, ensure it contains clusters: - cluster: {server: ..., certificate-authority-data: ...} with correct indentation
- Inspect the parsed structure: log the YAML and confirm /clusters/0/cluster is a mapping before calling
- Upgrade testcontainers-k3s to a version whose bundled kubeconfig matches the code path
Example fix
// before (custom kubeconfig with wrong shape)
clusters: []
// after
clusters:
- name: default
cluster:
server: https://127.0.0.1:6443
certificate-authority-data: <base64> Defensive patterns
Strategy: validation
Validate before calling
ObjectNode cfg = new ObjectMapper(new YAMLFactory()).readValue(kubeConfigYaml, ObjectNode.class);
if (!cfg.at("/clusters/0/cluster").isObject()) {
throw new IllegalStateException("kubeconfig template missing clusters[0].cluster mapping");
} Try / catch
try {
String yaml = k3s.generateInternalKubeConfigYaml(alias);
} catch (IllegalStateException e) {
if (e.getMessage().contains("expected to be an object")) {
// regenerate with default bundled kubeconfig or fix the YAML template
}
throw e;
} Prevention
- Do not modify the embedded kubeconfigYaml template unless keeping clusters[0].cluster as a mapping
- Validate custom kubeconfig YAML with a linter before using it in tests
- Pin testcontainers-k3s versions to avoid template drift
- Add a smoke test calling generateInternalKubeConfigYaml in CI
When it happens
Trigger: kubeConfigWithServerUrl invoked (directly from generateInternalKubeConfigYaml or via containerIsStarted config update) with a kubeConfigYaml template whose 'clusters[0].cluster' entry is missing, null, or not a mapping — e.g. after editing/replacing the default kubeConfigYaml resource or an incompatible YAML layout.
Common situations: Overriding the embedded kubeconfig YAML with a custom file having a different structure; the clusters list is empty; YAML indentation mistakes making 'cluster' a scalar/string; library version whose bundled template changed.
Related errors
AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12).
Data as JSON: /api/errors/d22b5312110017e3.
Report an issue: GitHub.
Appendix: source
Thrown at modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java:97
*/
public String generateInternalKubeConfigYaml(String networkAlias) {
if (this.getNetworkAliases().contains(networkAlias)) {
String serverUrl = "https://" + networkAlias + ":" + KUBE_SECURE_PORT;
return kubeConfigWithServerUrl(kubeConfigYaml, serverUrl);
} else {
throw new IllegalArgumentException(networkAlias + " is not a network alias for k3s container");
}
}
@SneakyThrows
private String kubeConfigWithServerUrl(String kubeConfigYaml, String serverUrl) {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
ObjectNode kubeConfigObjectNode = objectMapper.readValue(kubeConfigYaml, ObjectNode.class);
JsonNode clusterNode = kubeConfigObjectNode.at("/clusters/0/cluster");
if (!clusterNode.isObject()) {
throw new IllegalStateException("'/clusters/0/cluster' expected to be an object");
}
ObjectNode clusterConfig = (ObjectNode) clusterNode;
clusterConfig.replace("server", new TextNode(serverUrl));
kubeConfigObjectNode.set("current-context", new TextNode("default"));
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(kubeConfigObjectNode);
}
}
View on GitHub (pinned to 8e549514e3)