Summary
The logs-chargeback.billing_cluster_cost transform fails with a Painless script error when ess.billing.deployment_tags is indexed as a single string instead of an array. The runtime mapping for deployment_group uses for (tag in params._source.ess.billing.deployment_tags), which throws when the value is a string.
Step of instructions
Transform setup / runtime phase – the transform runs (or is validated) and fails when querying source indices where deployment_tags is a single string.
Error message
Transform encountered an exception: [Validation Failed: 1: Failed to test query, received status: BAD_REQUEST;]; Will automatically retry [1/-1]
org.elasticsearch.common.ValidationException: Validation Failed: 1: Failed to test query, received status: BAD_REQUEST;
...
Caused by: org.elasticsearch.action.search.SearchPhaseExecutionException: all shards failed
...
Caused by: org.elasticsearch.script.ScriptException: runtime error
Caused by: java.lang.IllegalArgumentException: Cannot iterate over [java.lang.String]
Query / context
The transform’s runtime_mappings.deployment_group.script.source runs over the billing source indices. The failure happens when the transform tests its query (e.g. during start or validation).
Elastic Stack version
9.2.2 (from stack trace)
Steps to reproduce
- Ingest ESS Billing (or on-prem billing) data where
ess.billing.deployment_tags is sometimes stored as a single string (e.g. one tag) rather than an array.
- Install/run the chargeback integration and the
billing_cluster_cost transform.
- The transform fails with the script error above when it runs or validates the query.
Expected behavior
The transform should run successfully whether deployment_tags is an array of strings or a single string. The runtime script should handle both shapes and still extract chargeback_group:<name> when present.
Root cause
The runtime script assumes params._source.ess.billing.deployment_tags is always iterable (a list). When the source sends or maps deployment_tags as a single string, Painless throws because you cannot use for (tag in ...) on a String.
Fix
Handle both List and String in the Painless script:
if (params._source?.ess?.billing?.deployment_tags != null) {
def tags = params._source.ess.billing.deployment_tags;
if (tags instanceof List) {
for (tag in tags) {
if (tag.startsWith('chargeback_group:')) {
emit(tag.substring('chargeback_group:'.length()));
return;
}
}
} else if (tags instanceof String) {
if (tags.startsWith('chargeback_group:')) {
emit(tags.substring('chargeback_group:'.length()));
return;
}
}
}
emit('');
Apply this as the runtime_mappings.deployment_group.script.source in the billing_cluster_cost transform definition (e.g. in the transform YAML that ships with the chargeback integration in the integrations repo: packages/chargeback/elasticsearch/transform/billing_cluster_cost/transform.yml).
References
- Integrations repo: chargeback transform at
packages/chargeback/elasticsearch/transform/billing_cluster_cost/transform.yml
- Related: deployment group extraction from tags
chargeback_group:<group_name>
Summary
The
logs-chargeback.billing_cluster_costtransform fails with a Painless script error wheness.billing.deployment_tagsis indexed as a single string instead of an array. The runtime mapping fordeployment_groupusesfor (tag in params._source.ess.billing.deployment_tags), which throws when the value is a string.Step of instructions
Transform setup / runtime phase – the transform runs (or is validated) and fails when querying source indices where
deployment_tagsis a single string.Error message
Query / context
The transform’s
runtime_mappings.deployment_group.script.sourceruns over the billing source indices. The failure happens when the transform tests its query (e.g. during start or validation).Elastic Stack version
9.2.2 (from stack trace)
Steps to reproduce
ess.billing.deployment_tagsis sometimes stored as a single string (e.g. one tag) rather than an array.billing_cluster_costtransform.Expected behavior
The transform should run successfully whether
deployment_tagsis an array of strings or a single string. The runtime script should handle both shapes and still extractchargeback_group:<name>when present.Root cause
The runtime script assumes
params._source.ess.billing.deployment_tagsis always iterable (a list). When the source sends or mapsdeployment_tagsas a single string, Painless throws because you cannot usefor (tag in ...)on a String.Fix
Handle both
ListandStringin the Painless script:Apply this as the
runtime_mappings.deployment_group.script.sourcein the billing_cluster_cost transform definition (e.g. in the transform YAML that ships with the chargeback integration in the integrations repo:packages/chargeback/elasticsearch/transform/billing_cluster_cost/transform.yml).References
packages/chargeback/elasticsearch/transform/billing_cluster_cost/transform.ymlchargeback_group:<group_name>