{"id":"980cd31f4617bf99","repo":"apache/kafka","slug":"for-templatename-runtime-defined-metric-tags","errorCode":null,"errorMessage":"For '{templateName}', runtime-defined metric tags do not match the tags in the template. Runtime = {runtimeTagKeys} Template = {templateTagKeys}","messagePattern":"For '(.+?)', runtime-defined metric tags do not match the tags in the template\\. Runtime = (.+?) Template = (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/metrics/Metrics.java","lineNumber":677,"sourceCode":"\n    /* For testing use only. */\n    Map<Sensor, List<Sensor>> childrenSensors() {\n        return Collections.unmodifiableMap(childrenSensors);\n    }\n\n    public MetricName metricInstance(MetricNameTemplate template, String... keyValue) {\n        return metricInstance(template, MetricsUtils.getTags(keyValue));\n    }\n\n    public MetricName metricInstance(MetricNameTemplate template, Map<String, String> tags) {\n        // check to make sure that the runtime defined tags contain all the template tags.\n        Set<String> runtimeTagKeys = new HashSet<>(tags.keySet());\n        runtimeTagKeys.addAll(config().tags().keySet());\n        \n        Set<String> templateTagKeys = template.tags();\n        \n        if (!runtimeTagKeys.equals(templateTagKeys)) {\n            throw new IllegalArgumentException(\"For '\" + template.name() + \"', runtime-defined metric tags do not match the tags in the template. \"\n                    + \"Runtime = \" + runtimeTagKeys + \" Template = \" + templateTagKeys.toString());\n        }\n                \n        return this.metricName(template.name(), template.group(), template.description(), tags);\n    }\n\n    /**\n     * Close this metrics repository.\n     */\n    @Override\n    public void close() {\n        if (this.metricsScheduler != null) {\n            this.metricsScheduler.shutdown();\n            try {\n                this.metricsScheduler.awaitTermination(30, TimeUnit.SECONDS);\n            } catch (InterruptedException ex) {\n                // ignore and continue shutdown\n                Thread.currentThread().interrupt();","sourceCodeStart":659,"sourceCodeEnd":695,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/metrics/Metrics.java#L659-L695","documentation":"Thrown by Metrics.metricInstance when the runtime tag key set supplied to instantiate a MetricNameTemplate does not exactly equal the template's declared tag key set. Templates exist to enforce uniform metric tag schemas across the codebase; metricInstance compares runtime keys (merged with the global MetricConfig.tags()) against template.tags() and rejects any drift (missing or extra keys).","triggerScenarios":"Calling metrics.metricInstance(template, \"k1\", \"v1\", \"k2\", \"v2\") where the template declares a different key set (e.g. {k1,k3}). The check at Metrics.java:676 throws IllegalArgumentException listing both the runtime and template key sets so the mismatch is visible.","commonSituations":"Adding a new tag to a MetricNameTemplate but forgetting to update every call site that builds a metric from it (or vice versa). Removing a tag from the template while leaving legacy callers passing it. Relying on MetricConfig.tags() to supply some keys but then changing which keys are global vs per-call. Refactoring metric tag schemas across versions without updating all producers of that metric.","solutions":["Read the error message: it lists Runtime = {...} and Template = {...}; add the missing keys and remove the extras from your metricInstance call.","If the template is what should change, update MetricNameTemplate's tags to match the desired schema and audit every call site.","Keep template tag schemas in a single constants class and have callers reference the same keys to avoid drift.","If a tag should be applied globally to every metric, put it in MetricConfig.tags() rather than passing it per-call."],"exampleFix":"// before: MetricName n = metrics.metricInstance(template, \"client-id\", clientId);  // template expects {node-id}\n// after:  MetricName n = metrics.metricInstance(template, \"node-id\", nodeId);","handlingStrategy":"validation","validationCode":"// Build the exact tag set the template expects, then call metricInstance.\nSet<String> expected = template.tags();\nMap<String,String> tags = new LinkedHashMap<>();\nfor (String k : expected) tags.put(k, resolveValue(k));\n// merge with MetricConfig-level tags to compare apples-to-apples\nSet<String> runtime = new HashSet<>(tags.keySet());\nruntime.addAll(metrics.config().tags().keySet());\nif (!runtime.equals(expected)) {\n    throw new IllegalArgumentException(\"Tag mismatch for \" + template.name() + \": runtime=\" + runtime + \" template=\" + expected);\n}\nmetrics.metricInstance(template, tags);","typeGuard":"// Narrow to tag-set-equality before invoking metricInstance.\nboolean tagsMatchTemplate(Metrics metrics, MetricNameTemplate t, Map<String,String> tags) {\n    Set<String> runtime = new HashSet<>(tags.keySet());\n    runtime.addAll(metrics.config().tags().keySet());\n    return runtime.equals(t.tags());\n}","tryCatchPattern":"try {\n    MetricName mn = metrics.metricInstance(template, tags);\n} catch (IllegalArgumentException e) {\n    // recompute tag set from template.tags() and retry once.\n}","preventionTips":["Always derive runtime tag keys from template.tags() rather than hardcoding them.","Account for MetricConfig.tags() — they are merged in and can cause mismatches.","Keep tag keys stable across versions; adding a template tag without updating callers breaks metricInstance.","Write a small helper that builds tags from a template + value map to avoid drift."],"tags":["metrics","metric-name-template","tags","validation"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}