No more +/- validation errors: One parenthesis rule for AJO wait-node arithmetic expressions
If you have ever pasted a carefully crafted date-time expression into a Journey wait node and been greeted by a vague parser error about the plus sign, you are not alone. Journey Optimizer’s expression language is strict about how arithmetic is written, and one missing pair of parentheses is enough to block your journey.
In this short guide, we walk through a real pattern: computing a wait-until time from profile data and “days from today,” then show why the expression fails and the minimal syntactic change that makes it valid.
What you are trying to do
A common pattern is to combine currentTimeInMillis() with a millisecond offset derived from a profile reminder date, today’s date at midnight, and a fixed offset (for example, fourteen days in milliseconds). Conceptually that is straightforward arithmetic inside toDateTime / toDateTimeOnly.
The expression that triggers the error
Below is an example of the kind of nested expression teams often build for wait logic. It is semantically reasonable, but as written it can fail validation.
toDateTimeOnly(
toDateTime(
currentTimeInMillis()
+ (
(
toInteger(
toDateTime(
concat(
toString(
#{ExperiencePlatform.ProfileFieldGroup.profile._dcthree.EventReminders.all(currentDataPackField.id == supplementalId).at(0).reminderDate}
),
"T00:00:00.000"
)
)
)
-
toInteger(
toDateTime(
concat(
toString(toDateOnly(now())),
"T00:00:00.000"
)
)
)
) / 86400000 - 14
) * 86400000
)
)

Error message: “The expression is invalid: You cannot use the character '+' like this because it is a keyword of the language. Please, add quotes to fix your expression.”
Why it fails: arithmetic needs explicit grouping
AJO documents a subtle rule for the + operator in advanced journey conditions: numeric addition must be expressed with operands wrapped in parentheses when they are themselves expressions. The documentation illustrates that something like toDateTimeOnly(now() + toDuration("PT1H")) can look semantically valid while still being syntactically rejected.

The supported form wraps each operand, for example:
toDateTimeOnly((now()) + (toDuration("PT1H")))toDateTimeOnly((now()) - (toDuration("PT1H")))

The same rule applies to your millis arithmetic: treat currentTimeInMillis(), each toInteger(toDateTime(...)) sub-expression, and literal products such as 14 * 86400000 as operands that should be parenthesized so the parser never sees an ambiguous + in an invalid position.
For the full operator notes (including the + parenthesis requirement), see Adobe’s Journey Optimizer documentation on Operators.
Corrected expression
Applying the same parenthesis discipline throughout—including around currentTimeInMillis(), each date conversion block, and the fourteen-day offset—yields a form the validator accepts. The inner day math can also be rearranged to subtract (14 * 86400000) directly once you are working purely in milliseconds, which keeps the intent clear.
toDateTimeOnly(
toDateTime(
(currentTimeInMillis())
+
(
(
toInteger(
toDateTime(
concat(
toString(
#{
ExperiencePlatform.ProfileFieldGroup.profile._dcthree.EventReminders.all(
currentDataPackField.id == ${supplementalId}
).at(0).reminderDate
}
),
"T00:00:00.000"
)
)
)
)
-
(
toInteger(
toDateTime(
concat(
toString(toDateOnly(now())),
"T00:00:00.000"
)
)
)
)
-
(14 * 86400000)
)
)
)Adjust the profile path, collection filter, and supplemental identifier syntax to match your schema and how you pass context into the expression. The structural lesson—parenthesize operands to + and -—is what prevents the parser error.

Takeaways
- When combining function calls and arithmetic in wait nodes, prefer explicit parentheses around every operand to
+and-. - If you see the “+ is a keyword” style error, re-check nesting before chasing quote or escaping issues.
- Keep a link to the official Operators page handy when onboarding new journey authors.
