alibaba/spring-cloud-alibaba · error · NacosRuntimeException

4000

4000

Error message

AbstractConfigChangeListener only support basic java data type for yaml. If you want to listen key changes for custom classes, please use `Listener` to listener whole yaml configuration and parse it by yourself.

What it means

Thrown by ScaYamlConfigChangeParser (a Nacos YAML change parser) when SnakeYAML's SafeConstructor cannot materialize a value during config-change diffing — specifically when the YAMLException message starts with 'could not determine a constructor for the tag' or the exception is a ComposerException. SafeConstructor only accepts plain Java scalars/maps/lists, so custom YAML tags or non-basic types cannot be diffed; the parser raises NacosRuntimeException with code 4000 (NacosException.INVALID_PARAM) guiding the user to listen on the whole config instead. It occurs at config-refresh time when AbstractConfigChangeListener key-level diffing runs.

Source

Thrown at spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/ScaYamlConfigChangeParser.java:68

				oldMap = yaml.load(oldContent);
				oldMap = getFlattenedMap(oldMap);
			}
			if (StringUtils.isNotBlank(newContent)) {
				newMap = yaml.load(newContent);
				newMap = getFlattenedMap(newMap);
			}
		}
		catch (MarkedYAMLException e) {
			handleYamlException(e);
		}

		return filterChangeData(oldMap, newMap);
	}

	private void handleYamlException(MarkedYAMLException e) {
		String message = e.getMessage();
		if ((message != null && message.startsWith(INVALID_CONSTRUCTOR_ERROR_INFO)) || e instanceof ComposerException) {
			throw new NacosRuntimeException(NacosException.INVALID_PARAM,
					"AbstractConfigChangeListener only support basic java data type for yaml. If you want to listen "
							+ "key changes for custom classes, please use `Listener` to listener whole yaml configuration and parse it by yourself.",
					e);
		}
		throw e;
	}

	private Map<String, Object> getFlattenedMap(Map<String, Object> source) {
		Map<String, Object> result = new LinkedHashMap<>(128);
		buildFlattenedMap(result, source, null);
		return result;
	}

	private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, @Nullable String path) {

		for (Map.Entry<String, Object> e : source.entrySet()) {
			String key = e.getKey();
			if (StringUtils.isNotBlank(path)) {

View on GitHub (pinned to 115d590110)

Solutions

  1. Restrict YAML listened via key-level change listeners to basic Java data types (scalars, maps, lists).
  2. For custom classes, register a whole-config Listener, receive the raw YAML, and parse/diff it yourself (as the message instructs).
  3. Remove custom `!!tag` constructs from the published YAML or replace them with plain maps.

Example fix

// before: key-level listener on YAML with a custom tag
dataId=app.yml  content:  service: !!com.example.Endpoint { host: x }
// after: plain basic types, or use a whole-config Listener
service:
  host: x
// (and parse/diff custom classes yourself via com.alibaba.nacos.api.config.listener.Listener)
Defensive patterns

Strategy: try-catch

Validate before calling

// Before relying on key-level YAML diff, validate the YAML loads under SafeConstructor with no custom tags.
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
try {
    yaml.load(publishedYaml);
} catch (MarkedYAMLException e) {
    // do not use key-level change listener for this content
}

Type guard

static boolean isSafeBasicYaml(String content) {
    try {
        Object o = new Yaml(new SafeConstructor(new LoaderOptions())).load(content);
        return o == null || o instanceof Map;
    } catch (RuntimeException e) {
        return false;
    }
}

Try / catch

try {
    // key-level change handling
} catch (NacosRuntimeException e) {
    if (e.getCode() == NacosException.INVALID_PARAM) {
        // fall back to whole-config Listener and parse/diff yourself
    }
}

Prevention

When it happens

Trigger: A Nacos YAML config that contains a custom/complex tag (e.g., !!com.example.MyType {...}), an anchor/alias structure SnakeYAML's SafeConstructor rejects, or any non-basic Java type, is changed while a key-level config-change listener (AbstractConfigChangeListener / @NacosConfigKeysChangeListener on yaml) is registered. On the next publish, the diff parser fails.

Common situations: Publishing YAML with custom Java object tags expected to bind to @ConfigurationProperties; using anchors/merge keys that the safe parser can't flatten; binding custom classes from YAML while relying on key-level change notifications.

Related errors


AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14). Data as JSON: /api/errors/78a191f814532c3d. Report an issue: GitHub.