Skip to content

Dry-run rejected my OTTL expression

You wrote an OTTL statement in a Transform or Filter step, ran the dry-run, and the step came back with an error instead of a result.

This is the preview working as intended. The statement is parsed by the same OTTL engine the collector runs, so an expression rejected here would have been rejected on the host — with the difference that here it costs you nothing.

Every message starts with validate config: and then takes one of a small number of forms.

statement has invalid syntax: 1:6: unexpected token "="

Section titled “statement has invalid syntax: 1:6: unexpected token "="”

The statement does not parse. The two numbers are line:column, so 1:6 points at character 6 — start reading there.

The most common cause by far is writing an assignment where OTTL wants a function call:

body = "redacted"          # wrong — OTTL has no assignment operator
set(body, "redacted")      # right

A truncated where clause produces the same shape, with the column at the end of the statement:

set(body, "x") where attributes["k"] ==     # unexpected token "<EOF>"

An unterminated string literal — a quote you opened and never closed. The quoted fragment in the message is the text the lexer choked on, which is everything from the opening quote onward.

The function does not exist in OTTL. Check the spelling, and check that you are not reaching for a function from a different tool. The functions you are most likely to want:

To do this Use
Set a field to a value set(field, value)
Replace regex matches inside a string replace_pattern(field, pattern, replacement)
Replace a whole matching value replace_match(field, pattern, replacement)
Remove attribute keys delete_key(attributes, "key")
Keep only some attribute keys keep_keys(attributes, ["a", "b"])

To redact PII, prefer the PII Masking processor over hand-written statements — it carries maintained detectors and can be previewed the same way. See Mask PII.

segment "span" from path "log.span.name" is not a valid path

Section titled “segment "span" from path "log.span.name" is not a valid path”

The field does not exist in the context you chose. The message ends with the context it checked — the log context above.

Note that the path in the message is log.span.name even though you wrote span.name: the context you selected is prefixed to every path in the statement. That prefix is why an expression copied from a trace pipeline fails in a log one — span.name is a real path in the span context and nothing at all in the log context.

Fix it by choosing the right context for the step, or the right field for the context you are in. In the log context the fields you will use most are body, severity_text, severity_number, attributes[...], resource.attributes[...], and time_unix_nano.

incorrect number of arguments. Expected: 2 Received: 1

Section titled “incorrect number of arguments. Expected: 2 Received: 1”

The function exists but you gave it the wrong number of arguments — set(body) where set needs a field and a value. The counts in the message are exact; no argument is optional here.

the regex pattern supplied to replace_pattern '"([a-z"' is not a valid pattern

Section titled “the regex pattern supplied to replace_pattern '"([a-z"' is not a valid pattern”

The statement parsed, but the regular expression inside it did not compile. The trailing part of the message is the regex engine’s own error (missing closing ], missing closing ), and so on) and names the defect precisely.

Two things to watch for:

  • Escaping. A backslash usually has to survive both the config format and the regex, so a literal dot is often \\. rather than \..
  • The dialect. Collectors use RE2, which has no backreferences and no lookaround. A pattern copied from Perl, Python, or JavaScript may be valid there and rejected here.

When the statement is valid but the result is wrong

Section titled “When the statement is valid but the result is wrong”

If there is no error and the output still isn’t what you wanted:

  • Nothing changed. The statement ran and matched nothing. Check the sample really contains what you think — a where clause that never holds produces exactly this, silently.
  • Everything vanished. A Filter step drops records where its expression is true, so an inverted condition drops the whole batch and the panel marks the chain as ending in a drop. Note that a route input filter has the opposite sense — it keeps what matches. An expression moved from one to the other has to be negated.
  • The step says “Unsupported”. The step is not a dry-run failure at all — that processor type has no preview implementation yet, and the record passes through untouched. It will still run on the collector.
  • A masking rule with a salted hash refuses to preview. That is deliberate: the salt is resolved only at delivery, so any digest shown here would be computed with the wrong salt. Test the same rule in full redaction mode to confirm what it finds, then switch back.