apache/dolphinscheduler · error · IllegalArgumentException

Could not resolve placeholder '

Error message

Could not resolve placeholder '

What it means

PropertyPlaceholderHelper throws this when a ${placeholder} in a value cannot be resolved by any supplied PropertyResolver and ignoreUnresolvablePlaceholders is false. The raw value is returned unexpanded instead of being silently passed through, since leaving unknown ${...} in task parameters is usually a bug. The message names the unresolvable placeholder and the value containing it.

Source

Thrown at dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parser/PropertyPlaceholderHelper.java:169

                        if (propVal == null) {
                            propVal = defaultValue;
                        }
                    }
                }
                if (propVal != null) {
                    // Recursive invocation, parsing placeholders contained in the
                    // previously resolved placeholder value.
                    propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                    result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                    if (log.isTraceEnabled()) {
                        log.trace("Resolved placeholder '" + placeholder + "'");
                    }
                    startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
                } else if (this.ignoreUnresolvablePlaceholders) {
                    // Proceed with unprocessed value.
                    startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
                } else {
                    throw new IllegalArgumentException("Could not resolve placeholder '"
                            + placeholder + "'" + " in value \"" + value + "\"");
                }
                visitedPlaceholders.remove(originalPlaceholder);
            } else {
                startIndex = -1;
            }
        }

        return result.toString();
    }

    private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
        int index = startIndex + this.placeholderPrefix.length();
        int withinNestedPlaceholder = 0;
        while (index < buf.length()) {
            if (substringMatch(buf, index, this.placeholderSuffix)) {
                if (withinNestedPlaceholder > 0) {
                    withinNestedPlaceholder--;

View on GitHub (pinned to 02eac45a1b)

Solutions

  1. Read the placeholder name in the message and define it in the task's local/custom parameters or as a global parameter on the workflow.
  2. Fix typos and case-sensitivity: placeholder names are resolved exactly against the parameter map.
  3. Verify the time/builtin parameter you expect is generated for that task type; use the format actually supported (e.g. $[yyyyMMdd] for time expressions vs ${var} for properties).
  4. If the placeholder is intended to stay literal, construct the helper with ignoreUnresolvablePlaceholders=true.
  5. Check upstream parameter passing (dependent/passthrough params) — the parent task may not have produced the parameter.

Example fix

// before
String sql = helper.replacePlaceholders("SELECT * FROM t WHERE d='${bizdate}'", resolver); // bizdate undefined
// after
props.setProperty("bizdate", "20240101"); // or define global param bizdate on the workflow
String sql = helper.replacePlaceholders("SELECT * FROM t WHERE d='${bizdate}'", resolver);
Defensive patterns

Strategy: validation

Validate before calling

Matcher m = Pattern.compile("\\$\\{([^}]+)}").matcher(raw);
while (m.find()) { if (!params.containsKey(m.group(1))) throw new IllegalArgumentException("undefined param: " + m.group(1)); }

Try / catch

try { return helper.replacePlaceholders(value, resolver); } catch (IllegalArgumentException e) { log.error("unresolved placeholder in {}", value, e); throw new TaskException("parameter resolution failed", e); }

Prevention

When it happens

Trigger: Calling replacePlaceholders/parseStringValue on a string containing ${name} where no property named 'name' exists in the resolver (local params, global params, or builtin time params); a typo like ${dateime} instead of ${datetime}; a placeholder that nested-resolution turned into a key absent from the parameter map.

Common situations: Task parameters referencing a global parameter that was deleted or renamed; system/builtin params (e.g. ${system.biz.date}) not injected in the execution context; user-defined params with case mismatches; switching task types so previously-defined params disappear.

Understand the failure class

Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.

Related errors


AI-assisted analysis of apache/dolphinscheduler@02eac45a1b (2026-09-06). Data as JSON: /api/errors/535aad2c58c85d35. Report an issue: GitHub.