testcontainers/testcontainers-java · error · IllegalStateException
Cannot create service account token in external mode
Error message
Cannot create service account token in external mode
What it means
createKibanaServiceAccountToken runs the elasticsearch-service-tokens tool inside the managed Elasticsearch container, so it requires a managed (non-null) ElasticsearchContainer reference. In external mode — when KibanaContainer is constructed only with an image and points at an already-running ES — there is no container to exec into, so the library throws this IllegalStateException instead of returning a bogus token.
Solutions
- Remove the token() call in external mode; instead supply credentials for the external cluster yourself (e.g. generate the service token out-of-band and pass it via withEnv).
- Switch to managed mode by constructing KibanaContainer with a started ElasticsearchContainer if you need the library to mint tokens.
- Guard the call: only invoke token() when kibana was built in managed mode.
Example fix
// before
KibanaContainer kibana = new KibanaContainer(image); // external mode
String token = kibana.token(); // throws
// after
// external mode: provide config without token()
kibana.withEnv("ELASTICSEARCH_SERVICEACCOUNTTOKEN", System.getenv("MY_KIBANA_TOKEN")); Defensive patterns
Strategy: type-guard
Type guard
// only fetch tokens in managed mode
boolean isManagedMode(KibanaContainer kibana) {
// track how you built it
return kibanaManagedFlag; // set true when constructed with an ElasticsearchContainer
}
if (!isManagedMode(kibana)) return externalTokenFromEnv(); Prevention
- Document per test class whether Kibana is managed or external and gate token() calls accordingly.
- In external mode, provision service tokens out-of-band and inject them via withEnv.
- Keep managed and external test fixtures in separate base classes.
When it happens
Trigger: Calling kibana.token() on a KibanaContainer created in external mode (no ElasticsearchContainer passed / elasticsearch == null), e.g. new KibanaContainer(image).withEnv("ELASTICSEARCH_HOSTS", ...) pointing at an external cluster.
Common situations: Copy-pasting managed-mode sample code (which calls token()) into a test that connects to an external/self-hosted Elasticsearch; switching a test from managed to external mode but leaving the token() call in place.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Failed to create Kibana service account token. Exit code
- withReuse(true) is not supported for KibanaContainer in…
- Cannot set Elasticsearch URL when using Elasticsearch…
- Elasticsearch containerId is not available. In managed…
- Failed to connect Elasticsearch container to ad-hoc shared…
AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12).
Data as JSON: /api/errors/3a6ca544082fbb1c.
Report an issue: GitHub.
Appendix: source
Thrown at modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/KibanaContainer.java:533
}
}
private static void ensureCompatibleVersion(String esVersion) {
ComparableVersion comparableVersion = new ComparableVersion(esVersion);
if (comparableVersion.isLessThan(MINIMUM_SUPPORTED_VERSION)) {
throw new IllegalArgumentException(
String.format(
"Kibana version %s is not supported. Minimum version is %s",
comparableVersion,
MINIMUM_SUPPORTED_VERSION
)
);
}
}
private String createKibanaServiceAccountToken(String protocol) {
if (elasticsearch == null) {
throw new IllegalStateException("Cannot create service account token in external mode");
}
String elasticPassword = elasticsearch
.getEnvMap()
.getOrDefault("ELASTIC_PASSWORD", ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD);
// Create a unique token name to avoid collisions if the same ES container is reused.
String tokenName = "tc-kibana-" + Base58.randomString(12);
String endpoint = protocol + "://localhost:9200/_security/service/elastic/kibana/credential/token/" + tokenName;
return Unreliables.retryUntilSuccess(
45,
TimeUnit.SECONDS,
() -> {
String curlTlsArgs = "";
if ("https".equals(protocol)) {
// In managed HTTPS mode, use the Elasticsearch HTTP CA for curl.View on GitHub (pinned to 8e549514e3)