alibaba/spring-ai-alibaba · error · IllegalArgumentException

The template string is not valid.

Error message

The template string is not valid.

What it means

SaaStTemplateRenderer.createST() wraps StringTemplate (ST) construction in a catch-all that rethrows any parsing/processing failure as IllegalArgumentException("The template string is not valid.", ex). The template text could not be turned into a valid ST instance with the configured delimiters.

Solutions

  1. Fix the template syntax: close all '{var}' expressions and escape literal delimiters
  2. Check the rendered output — the cause (ex.getCause()) names the exact ST syntax problem
  3. Use the renderer's JSON placeholder handling or choose delimiters that don't collide with prompt content (e.g. '<' '>' or '$' '$')
  4. Validate templates at startup with a dry-run render instead of failing at request time

Example fix

// before
renderer.render("User data: { \"name\": \"bob\" }"); // { } collide with delimiters
// after
SaaStTemplateRenderer.builder()
    .startDelimiterToken("$").endDelimiterToken("$")
    .build();
// template: "User data: { \"name\": \"bob\" }, hello $name$"
Defensive patterns

Strategy: try-catch

Validate before calling

boolean templateParses(String tpl) { try { new STGroupDelim().InstanceOf(new STGroup('{','}'), tpl); return true; } catch (Exception e) { return false; } }

Type guard

boolean isRenderable(String tpl) { return tpl != null && tpl.chars().filter(c -> c == '{').count() == tpl.chars().filter(c -> c == '}').count(); }

Try / catch

try { renderer.apply(template, vars); } catch (IllegalArgumentException e) { log.error("Bad template syntax: {}", e.getCause() != null ? e.getCause().getMessage() : e.getMessage()); throw new TemplateSyntaxException(template, e); }

Prevention

When it happens

Trigger: Calling the renderer with a template containing malformed ST syntax, unbalanced/unknown delimiters (custom start/end tokens), or characters that break ST parsing; nested braces from JSON conflicting with delimiters.

Common situations: Prompts containing JSON or code with { } colliding with the '{' '}' delimiter tokens; typos like unclosed '{name'; using a delimiter character that appears literally in the prompt; template loaded from config/user input with syntax errors.

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 alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/057a7447dcdaf2b2. Report an issue: GitHub.

Appendix: source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/renderer/SaaStTemplateRenderer.java:165

	}

	private ST createST(String template) {
		try {
			String processedTemplate = template;
			// If using string delimiters, convert them to single-char delimiters for ST
			if (this.useStringDelimiters) {
				processedTemplate = convertStringDelimitersToChar(template);
			}
			else {
				// For single-char delimiters, protect JSON content to avoid conflicts
				processedTemplate = protectJsonContent(template);
			}
			STGroup group = new STGroup(this.startDelimiterToken, this.endDelimiterToken);
			group.setListener(new Slf4jStErrorListener(logger));
			return new ST(group, processedTemplate);
		}
		catch (Exception ex) {
			throw new IllegalArgumentException("The template string is not valid.", ex);
		}
	}

	// Temporary placeholders for JSON braces
	private static final String JSON_OPEN_PLACEHOLDER = "\uE000";
	private static final String JSON_CLOSE_PLACEHOLDER = "\uE001";

	/**
	 * Protects JSON content in templates when using single-character delimiters.
	 * This method identifies JSON objects/arrays and replaces their braces with placeholders
	 * to prevent StringTemplate from treating them as template variables.
	 * @param template the original template
	 * @return template with JSON braces protected
	 */
	private String protectJsonContent(String template) {
		StringBuilder result = new StringBuilder();
		int i = 0;
		int len = template.length();

View on GitHub (pinned to f82da0b50f)