alibaba/spring-ai-alibaba · info · RuntimeException
failed to create dir: + saveFile.getParentFile()
Error message
failed to create dir: + saveFile.getParentFile()
What it means
HumanInTheLoopHook.validateFeedback() logs (non-fatal) any ToolFeedback entries that do not correspond to any tool call currently awaiting approval (no matching name+id). These entries are ignored; validation continues based only on the pending calls.
Source
Thrown at spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/base/manager/FileManager.java:96
.append(DateUtils.getYear())
.append(File.separator)
.append(DateUtils.getMonth())
.append(File.separator)
.append(DateUtils.getDay())
.append(File.separator)
.append(IdGenerator.uuid32())
.append("_")
.append(System.currentTimeMillis())
.append(".")
.append(FilenameUtils.getExtension(file.getOriginalFilename()))
.toString();
File saveFile = new File(storagePath + File.separator + filePath);
try {
if (!saveFile.getParentFile().exists()) {
boolean success = saveFile.getParentFile().mkdirs();
if (!success) {
throw new RuntimeException("failed to create dir: " + saveFile.getParentFile());
}
}
file.transferTo(saveFile);
}
catch (IOException e) {
throw new RuntimeException(e);
}
return filePath;
}
/**
* Loads a file from the storage system.
* @param filePath The relative path of the file (path traversal sequences are rejected)
* @return A Resource object representing the file
* @throws BizException if the file is not found or path is invalid (e.g. path traversal)
*/View on GitHub (pinned to f82da0b50f)
Solutions
- Build feedback strictly from the tool calls listed in the current InterruptionMetadata
- Clear/reset client-side cached approvals after each resume
- Verify tool names and ids match the current AssistantMessage's pending calls
- Treat this warning as a signal of stale state and regenerate the interrupt metadata
Example fix
// before
feedback.add(approvalForLastTurnsToolCall); // id from a previous interrupt
// after
List<ToolFeedback> feedback = metadata.toolCalls().stream()
.map(call -> approve(call.name(), call.id()))
.toList(); // only calls pending in this interrupt Defensive patterns
Strategy: validation
Validate before calling
Set<String> pendingKeys = toolCallsNeedingApproval.stream().map(c -> c.name() + ":" + c.id()).collect(java.util.stream.Collectors.toSet());
List<ToolFeedback> stale = toolFeedbacks.stream()
.filter(tf -> !pendingKeys.contains(tf.getName() + ":" + tf.getId())).toList();
if (!stale.isEmpty()) log.info("Dropping stale feedback entries: {}", stale); Prevention
- Build feedback only from the current interrupt's tool calls
- Clear cached approvals in the client after each resume
- Log and prune duplicate submissions before resuming
When it happens
Trigger: The supplied toolFeedbacks list contains an entry whose name/id pair matches none of toolCallsNeedingApproval — e.g. feedback for a tool already answered, or ids from a stale interrupt.
Common situations: Clients replaying old feedback payloads; UI caching approvals across turns; duplicate submission of the same approval; renames of tool functions between runs.
Related errors
- INVALID_PARAMS
- user define param: + userParamParam.getField() + is empty
- extra tool param should be map
- Human feedback metadata must be of type InterruptionMetadata
- Failed to check interruptAfter hook for streaming node
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/a2c21fafe0413056.
Report an issue: GitHub.