apolloconfig/apollo · error · BadRequestException
{ex.getMessage()}
Error message
{ex.getMessage()} What it means
This is a server-side validation error raised by Apollo Portal when a user submits YAML/YML namespace config text that SnakeYAML cannot parse. NamespaceTextSyntaxChecker.check() feeds the text into a YamlPropertiesFactoryBean whose createYaml() enables strict mode (allowDuplicateKeys=false) and a SafeConstructor, so any malformed YAML, duplicate keys, or disallowed types surface as a generic Exception whose message is re-thrown as a Spring BadRequestException (HTTP 400). Because the original exception type is erased and only getMessage() is forwarded, the client sees the raw SnakeYAML parser message (often prefixed with 'while scanning... / found... / mapping values are not allowed here').
Source
Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/util/NamespaceTextSyntaxChecker.java:55
private NamespaceTextSyntaxChecker() {}
public static void check(NamespaceTextModel model) {
if (StringUtils.isBlank(model.getConfigText())) {
return;
}
if (model.getFormat() != ConfigFileFormat.YAML && model.getFormat() != ConfigFileFormat.YML) {
return;
}
TypeLimitedYamlPropertiesFactoryBean yamlPropertiesFactoryBean =
new TypeLimitedYamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(
new ByteArrayResource(model.getConfigText().getBytes(StandardCharsets.UTF_8)));
try {
yamlPropertiesFactoryBean.getObject();
} catch (Exception ex) {
throw new BadRequestException(ex.getMessage());
}
}
private static class TypeLimitedYamlPropertiesFactoryBean extends YamlPropertiesFactoryBean {
@Override
protected Yaml createYaml() {
LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setAllowDuplicateKeys(false);
DumperOptions dumperOptions = new DumperOptions();
return new Yaml(new SafeConstructor(loaderOptions), new Representer(dumperOptions),
dumperOptions, loaderOptions);
}
}
}
View on GitHub (pinned to d95fc18d11)
Solutions
- Run the submitted YAML through a local parser with the same settings (allowDuplicateKeys=false, SafeConstructor) before POSTing — e.g. `new Yaml(new SafeConstructor(new LoaderOptions())).loadAll(text)` in a scratch test — and fix whatever message comes back.
- Run `yamllint` (Python) or an IDE YAML validator on the configText to catch indentation/tab/syntax issues before submission.
- Remove duplicate keys: search the document for repeated keys at the same nesting level and rename or nest them.
- Strip any custom YAML tags (the `!!` or `!` constructs) and replace custom types with plain scalars/maps, because SafeConstructor intentionally refuses them.
- Normalize whitespace: convert tabs to 2-space indentation, replace smart quotes with ASCII quotes, ensure UTF-8 (the checker already re-encodes with UTF-8 but invisible characters can still break parsing).
- If the error message is ambiguous, reproduce locally by instantiating TypeLimitedYamlPropertiesFactoryBean (or just new Yaml(new SafeConstructor(new LoaderOptions(){{setAllowDuplicateKeys(false);}})) ) against the exact configText to see the full stack trace.
Example fix
// before (invalid: tab indentation + duplicate key) server: port: 8080 server: port: 9090 // after (2-space indent, single key) server: port: 8080
Defensive patterns
Strategy: validation
Validate before calling
// Validate YAML client-side with the same rules the server enforces
// (allowDuplicateKeys=false, SafeConstructor) before submitting.
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
public static void validateApolloYaml(String configText) {
if (configText == null || configText.isBlank()) return;
LoaderOptions opts = new LoaderOptions();
opts.setAllowDuplicateKeys(false);
Yaml yaml = new Yaml(new SafeConstructor(opts));
try {
// Mirror Spring's expectation: it flattens into Properties, so a flat
// map-shaped document is what parses cleanly.
yaml.loadAll(configText).forEach(o -> { /* no-op */ });
} catch (Exception e) {
throw new IllegalArgumentException("YAML rejected locally: " + e.getMessage(), e);
}
} Try / catch
// Server-side: NamespaceTextSyntaxChecker already wraps the parse failure
// in BadRequestException. Callers (controllers) should let it propagate so
// Spring maps it to HTTP 400, and only add context at the edge:
try {
NamespaceTextSyntaxChecker.check(model);
} catch (BadRequestException e) {
// surface the parser message to the API client as-is; do not swallow
throw e;
} Prevention
- Lint YAML in CI (yamllint or a SnakeYAML-based check) for any config pushed to Apollo.
- Standardize on 2-space indentation and forbid tabs via editorconfig/.editorconfig in config repos.
- When templating YAML (Helm, Jinja, envsubst), assert the output parses before committing.
- Avoid custom YAML tags in Apollo namespace text — SafeConstructor will reject them by design.
- Keep a unit test that round-trips your base YAML through new Yaml(new SafeConstructor(opts)) with setAllowDuplicateKeys(false) to mirror the server's exact strictness.
When it happens
Trigger: Calling the Portal WebAPI or OpenAPI endpoint that creates/updates a namespace whose model.format is ConfigFileFormat.YAML or YML with configText that: (a) is syntactically invalid YAML (tabs, bad indentation, unquoted colons); (b) contains duplicate top-level or nested keys — rejected because LoaderOptions.setAllowDuplicateKeys(false); (c) contains constructs the SafeConstructor refuses (e.g. !!python/object, unparseable merge keys, custom tags); (d) embeds a YAML scalar where a mapping is expected, or a document that does not resolve into a flat Properties-style map.
Common situations: Developers editing Apollo YAML config in the portal UI and accidentally using tabs instead of spaces; copy-pasting a YAML block that has Windows line endings or smart quotes; defining the same key twice across merged documents; pasting an application.yml that uses Spring-specific tags (e.g. !!timestamp, custom constructors) that SafeConstructor rejects; CI pipelines pushing YAML generated by templating tools that emit a stray '{{placeholder}}'; upgrading SnakeYAML across versions where DuplicateKeyException or new security restrictions on SafeConstructor start firing.
Related errors
- The maximum number of items (%s) for this namespace has been
- Config text has repeated keys: %s, please check your input.
- line:{} key value must separate by '='
- The App Id of path variable and request body is different
- Comment item's key or value should be blank.
AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14).
Data as JSON: /api/errors/bdd5db42f8825499.
Report an issue: GitHub.