apache/druid · error · ExpressionValidationException

second argument must be a base64 encoded STRING value but…

Error message

second argument must be a base64 encoded STRING value but got %s instead

What it means

In complex_decode_base64's eval, the second argument must decode to a base64-encoded STRING (or byte[]) of the declared complex type; if it is already a deserialized value of the matching complex type it is passed through, otherwise this validationFailed is thrown describing the actual type of the second argument.

Solutions

  1. Pass the column that holds base64-encoded STRING data.
  2. Ensure the declared complex type (arg 1) matches the type the column was serialized with.
  3. Cast the second argument to VARCHAR if it is numeric: complex_decode_base64('type', CAST(col AS VARCHAR)).
  4. Inspect the column's actual type via segment metadata or EXPLAIN.

Example fix

// before
complex_decode_base64('druid.metrics.snapshot', numeric_col)
// after
complex_decode_base64('druid.metrics.snapshot', base64_string_col)
Defensive patterns

Strategy: validation

When it happens

Trigger: Second argument evaluates to a non-string/non-byte[] value of a different type — e.g. a long, double, array, or a complex value of a mismatched complex type name.

Common situations: Pointing the function at the wrong column (raw long/JSON column instead of base64 string); decoding a column serialized with a different complex type than declared in arg 1.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/45428cd18fbe6b18. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/math/expr/BuiltInExprMacros.java:112

      @Override
      public ExprEval<?> eval(ObjectBinding bindings)
      {
        ExprEval<?> toDecode = args.get(1).eval(bindings);
        if (toDecode.value() == null) {
          return ExprEval.ofComplex(complexType, null);
        }
        final Object serializedValue = toDecode.value();
        final byte[] base64;
        if (serializedValue instanceof String) {
          base64 = StringUtils.decodeBase64String(toDecode.asString());
        } else if (serializedValue instanceof byte[]) {
          base64 = (byte[]) serializedValue;
        } else if (complexType.getComplexTypeName().equals(toDecode.type().getComplexTypeName())) {
          // pass it through, it is already the right thing
          return toDecode;
        } else {
          throw validationFailed(
              "second argument must be a base64 encoded STRING value but got %s instead",
              toDecode.type()
          );
        }

        return ExprEval.ofComplex(complexType, typeStrategy.fromBytes(base64));
      }

      @Nullable
      @Override
      public ExpressionType getOutputType(InputBindingInspector inspector)
      {
        return complexType;
      }

      @Override
      public boolean isLiteral()
      {

View on GitHub (pinned to 9b90983fd2)