{"record":{"id":"913b5b47c1162a67","repo":"apache/skywalking","slug":"tag-requires-exactly-one-string-literal-argument","errorCode":null,"errorMessage":"tag() requires exactly one string literal argument, e.g. tag(\"KEY\")","messagePattern":"tag\\(\\) requires exactly one string literal argument, e\\.g\\. tag\\(\"KEY\"\\)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/v2/compiler/LALValueCodegen.java","lineNumber":742,"sourceCode":"            generateBinaryExpression(sb, value.getConcatParts(),\n                value.getConcatOps(), genCtx);\n            return;\n        }\n\n        // Handle parenthesized expression: (innerExpr as Type).chain...\n        if (value.getParenInner() != null) {\n            generateParenAccess(sb, value, genCtx);\n            return;\n        }\n\n        // tag(\"KEY\") — reads LogData tags via LalRuntimeHelper.tagValue().\n        // Only valid when input is LogData.Builder (i.e. no inputType, or inputType\n        // is LogData.Builder).  For typed inputs, tag() is not supported — use\n        // parsed.* to access fields on the typed input.\n        if (\"tag\".equals(value.getFunctionCallName())) {\n            if (value.getFunctionCallArgs().size() != 1\n                    || !value.getFunctionCallArgs().get(0).getValue().isStringLiteral()) {\n                throw new IllegalArgumentException(\n                    \"tag() requires exactly one string literal argument, \"\n                        + \"e.g. tag(\\\"KEY\\\")\");\n            }\n            if (genCtx.inputType != null\n                    && !LALCodegenHelper.LOGDATA_BUILDER_CLASS\n                        .isAssignableFrom(genCtx.inputType)) {\n                throw new IllegalArgumentException(\n                    \"tag() reads LogData tags but the input type is \"\n                        + genCtx.inputType.getName()\n                        + \". Use a json{}/yaml{}/text{} parser, or access \"\n                        + \"typed fields via parsed.* instead.\");\n            }\n            sb.append(\"h.tagValue(\\\"\");\n            final String key = value.getFunctionCallArgs().get(0)\n                .getValue().getSegments().get(0);\n            sb.append(DslJavaSourceText.toLiteral(key)).append(\"\\\")\");\n            return;\n        }","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/v2/compiler/LALValueCodegen.java#L724-L760","documentation":"Thrown by MeterSystem.createInternal when the requested function exists but its AcceptableValue generic parameter does not equal the data type passed by the caller (e.g. the function is AcceptableValue<Long> and the creation path supplies Double). The meter framework generates a Javassist subclass binding the function to the metric's data type; mismatched generics cannot be compiled safely, so it fails fast with the expected vs. actual type names.","triggerScenarios":"A custom MeterSystem.create caller passing dataType=Double.class for a function declared AcceptableValue<Long>, or a MAL expression producing a value type inconsistent with the chosen function (e.g. a percent function expecting Long fed by a decimal-producing expression).","commonSituations":"Writing custom meter functions or direct MeterSystem integrations; changing a function's generic parameter without rebuilding dependent rules; MAL function selection that doesn't match the expression's numeric type.","solutions":["Align the data type: pass the type the function declares in AcceptableValue<T> (the error prints both type names)","Or switch to a function whose generic parameter matches your data type","Recompile custom functions and callers together so the generic contract stays consistent"],"exampleFix":"// before — function declares AcceptableValue<Long>\nmeterSystem.create(\"my_metric\", \"sum\", ScopeType.SERVICE);\n// custom create path passing Double\nclass LongSumFunction implements AcceptableValue<Long> { ... }\n// after — use a function declaring AcceptableValue<Double>\nclass DoubleSumFunction implements AcceptableValue<Double> { ... }","handlingStrategy":"type-guard","validationCode":"for (Type t : functionClass.getGenericInterfaces()) {\n    if (t instanceof ParameterizedType && ((ParameterizedType) t).getRawType().getTypeName().endsWith(\"AcceptableValue\")) {\n        Type arg = ((ParameterizedType) t).getActualTypeArguments()[0];\n        if (!arg.equals(dataType)) throw new IllegalArgumentException(\"type mismatch: \" + arg);\n    }\n}","typeGuard":"static boolean acceptsType(Class<? extends AcceptableValue<?>> fn, Class<?> dataType) {\n    for (Type t : fn.getGenericInterfaces()) {\n        if (t instanceof ParameterizedType) {\n            ParameterizedType p = (ParameterizedType) t;\n            if (p.getRawType().getTypeName().endsWith(\"AcceptableValue\")) {\n                return p.getActualTypeArguments()[0].equals(dataType);\n            }\n        }\n    }\n    return false;\n}","tryCatchPattern":null,"preventionTips":["Keep the AcceptableValue generic parameter of custom functions in sync with the creation call","The error message names both types — read expected vs actual before changing code"],"tags":["skywalking","oap","meter","generics","mal","contract"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}