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

  1. Build feedback strictly from the tool calls listed in the current InterruptionMetadata
  2. Clear/reset client-side cached approvals after each resume
  3. Verify tool names and ids match the current AssistantMessage's pending calls
  4. 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

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


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/a2c21fafe0413056. Report an issue: GitHub.