Skip to content

[Bug] AppendStrategy.apply() 单值追加分支原地修改 oldValue,导致历史快照 / checkpoint 状态污染 #4757

Description

@eye-gu

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

if (oldValueIsList) {
List<Object> oldList = (List<Object>) oldValue;
if (list != null) {
if (list.isEmpty()) {
return oldValue;
}
if (oldValueIsList) {
var result = evaluateRemoval((List<Object>) oldValue, list);
if (allowDuplicate) {
return Stream.concat(result.oldValues().stream(), result.newValues()
.stream())
.collect(Collectors.toList());
} else {
return Stream.concat(result.oldValues().stream(), result.newValues()
.stream())
.distinct()
.collect(Collectors.toList());
}
}
oldList.addAll(list);
}
else {
oldList.add(newValue);
}
return oldList;

AppendStrategy.apply(Object oldValue, Object newValue) 违反了 reducer 的无副作用契约:当 oldValue 是 List、newValue 是单个非集合值(即最常见的写法 Map.of("messages", "msg1"))时,它直接对传入的 oldList 执行 add(newValue) 并 return oldList,而不是新建列表。

这会让任何持有 oldValue 引用的调用方被污染,表现为历史快照、checkpoint、并行分支的状态隔离失效。

Expected Behavior

No response

Steps To Reproduce

// step1 → humanNode(interruptBefore) → step2
CompiledGraph graph = builder.compile(CompileConfig.builder()
.saverConfig(SaverConfig.builder().register(new MemorySaver()).build())
.interruptBefore("humanNode")
.build());

graph.invoke(Map.of(), config); // 执行 step1,在 humanNode 前中断;messages=[question]
graph.updateState(config, Map.of("messages", "user-answer"), null); // 注入反馈

// 期望:最早的 step1 历史快照 messages = [question](1 条)
// 实际:[question, user-answer](2 条)—— 历史 checkpoint 被污染

Environment

Spring AI Alibaba version(s):

Debug logs

No response

Anything else?

No response

Metadata

Metadata

Assignees

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions