Skip to content

Commit c34864e

Browse files
committed
feat: enhance error handling in ChromaApi with detailed logging for not found exceptions
Signed-off-by: liugddx <[email protected]>
1 parent d71869e commit c34864e

File tree

1 file changed

+130
-12
lines changed
  • vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/vectorstore

1 file changed

+130
-12
lines changed

vector-stores/spring-ai-chroma-store/src/main/java/org/springframework/ai/chroma/vectorstore/ChromaApi.java

Lines changed: 130 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
import com.fasterxml.jackson.core.JsonProcessingException;
2929
import com.fasterxml.jackson.databind.ObjectMapper;
3030

31+
import org.slf4j.Logger;
32+
import org.slf4j.LoggerFactory;
33+
3134
import org.springframework.ai.chroma.vectorstore.ChromaApi.QueryRequest.Include;
3235
import org.springframework.ai.chroma.vectorstore.common.ChromaApiConstants;
3336
import org.springframework.ai.util.json.JsonParser;
@@ -52,6 +55,8 @@
5255
*/
5356
public class ChromaApi {
5457

58+
private static final Logger logger = LoggerFactory.getLogger(ChromaApi.class);
59+
5560
public static Builder builder() {
5661
return new Builder();
5762
}
@@ -62,6 +67,9 @@ public static Builder builder() {
6267
// Regular expression pattern that looks for a message.
6368
private static final Pattern MESSAGE_ERROR_PATTERN = Pattern.compile("\"message\":\"(.*?)\"");
6469

70+
// Regular expression pattern that looks for NotFoundError in JSON error response.
71+
private static final Pattern NOT_FOUND_ERROR_PATTERN = Pattern.compile("NotFoundError\\('([^']*)'\\)");
72+
6573
private static final String X_CHROMA_TOKEN_NAME = "x-chroma-token";
6674

6775
private final ObjectMapper objectMapper;
@@ -136,12 +144,21 @@ public Tenant getTenant(String tenantName) {
136144
.retrieve()
137145
.body(Tenant.class);
138146
}
139-
catch (HttpServerErrorException | HttpClientErrorException e) {
140-
String msg = this.getErrorMessage(e);
141-
if (String.format("Tenant [%s] not found", tenantName).equals(msg)) {
147+
catch (HttpClientErrorException e) {
148+
if (isNotFoundError(e, "Tenant", tenantName)) {
149+
String errorMessage = this.getErrorMessage(e);
150+
if (StringUtils.hasText(errorMessage)) {
151+
logger.debug("Tenant [{}] does not exist: {}, returning null", tenantName, errorMessage);
152+
}
153+
else {
154+
logger.debug("Tenant [{}] does not exist, returning null", tenantName);
155+
}
142156
return null;
143157
}
144-
throw new RuntimeException(msg, e);
158+
throw new RuntimeException(this.getErrorMessage(e), e);
159+
}
160+
catch (HttpServerErrorException e) {
161+
throw new RuntimeException(this.getErrorMessage(e), e);
145162
}
146163
}
147164

@@ -165,12 +182,23 @@ public Database getDatabase(String tenantName, String databaseName) {
165182
.retrieve()
166183
.body(Database.class);
167184
}
168-
catch (HttpServerErrorException | HttpClientErrorException e) {
169-
String msg = this.getErrorMessage(e);
170-
if (msg.startsWith(String.format("Database [%s] not found.", databaseName))) {
185+
catch (HttpClientErrorException e) {
186+
if (isNotFoundError(e, "Database", databaseName)) {
187+
String errorMessage = this.getErrorMessage(e);
188+
if (StringUtils.hasText(errorMessage)) {
189+
logger.debug("Database [{}] in tenant [{}] does not exist: {}, returning null", databaseName,
190+
tenantName, errorMessage);
191+
}
192+
else {
193+
logger.debug("Database [{}] in tenant [{}] does not exist, returning null", databaseName,
194+
tenantName);
195+
}
171196
return null;
172197
}
173-
throw new RuntimeException(msg, e);
198+
throw new RuntimeException(this.getErrorMessage(e), e);
199+
}
200+
catch (HttpServerErrorException e) {
201+
throw new RuntimeException(this.getErrorMessage(e), e);
174202
}
175203
}
176204

@@ -226,12 +254,24 @@ public Collection getCollection(String tenantName, String databaseName, String c
226254
.retrieve()
227255
.body(Collection.class);
228256
}
229-
catch (HttpServerErrorException | HttpClientErrorException e) {
230-
String msg = this.getErrorMessage(e);
231-
if (String.format("Collection [%s] does not exists", collectionName).equals(msg)) {
257+
catch (HttpClientErrorException e) {
258+
if (isNotFoundError(e, "Collection", collectionName)) {
259+
String errorMessage = this.getErrorMessage(e);
260+
if (StringUtils.hasText(errorMessage)) {
261+
logger.debug(
262+
"Collection [{}] in database [{}] and tenant [{}] does not exist: {}, returning null",
263+
collectionName, databaseName, tenantName, errorMessage);
264+
}
265+
else {
266+
logger.debug("Collection [{}] in database [{}] and tenant [{}] does not exist, returning null",
267+
collectionName, databaseName, tenantName);
268+
}
232269
return null;
233270
}
234-
throw new RuntimeException(msg, e);
271+
throw new RuntimeException(this.getErrorMessage(e), e);
272+
}
273+
catch (HttpServerErrorException e) {
274+
throw new RuntimeException(this.getErrorMessage(e), e);
235275
}
236276
}
237277

@@ -326,7 +366,76 @@ private void httpHeaders(HttpHeaders headers) {
326366
}
327367
}
328368

369+
private boolean isNotFoundError(HttpClientErrorException e, String resourceType, String resourceName) {
370+
// First, check the response body for JSON error response
371+
String responseBody = e.getResponseBodyAsString();
372+
if (StringUtils.hasText(responseBody)) {
373+
try {
374+
ErrorResponse errorResponse = this.objectMapper.readValue(responseBody, ErrorResponse.class);
375+
String error = errorResponse.error();
376+
// Check for NotFoundError('message') format
377+
if (NOT_FOUND_ERROR_PATTERN.matcher(error).find()) {
378+
return true;
379+
}
380+
// Check for "Resource [name] not found." format
381+
String expectedPattern = String.format("%s [%s] not found.", resourceType, resourceName);
382+
if (error.startsWith(expectedPattern)) {
383+
return true;
384+
}
385+
// Check if error contains "not found" (case insensitive)
386+
if (error.toLowerCase().contains("not found")) {
387+
return true;
388+
}
389+
}
390+
catch (JsonProcessingException ex) {
391+
// If JSON parsing fails, check the response body directly
392+
String expectedPattern = String.format("%s [%s] not found.", resourceType, resourceName);
393+
if (responseBody.contains(expectedPattern) || responseBody.toLowerCase().contains("not found")) {
394+
return true;
395+
}
396+
}
397+
}
398+
399+
// Check the exception message
400+
String errorMessage = e.getMessage();
401+
if (StringUtils.hasText(errorMessage)) {
402+
// Check for "Resource [name] not found." format
403+
String expectedPattern = String.format("%s [%s] not found.", resourceType, resourceName);
404+
if (errorMessage.contains(expectedPattern)) {
405+
return true;
406+
}
407+
// Check if error message contains "not found" (case insensitive)
408+
if (errorMessage.toLowerCase().contains("not found")) {
409+
return true;
410+
}
411+
}
412+
413+
return false;
414+
}
415+
329416
private String getErrorMessage(HttpStatusCodeException e) {
417+
// First, try to parse the response body as JSON error response
418+
String responseBody = e.getResponseBodyAsString();
419+
if (StringUtils.hasText(responseBody)) {
420+
try {
421+
ErrorResponse errorResponse = this.objectMapper.readValue(responseBody, ErrorResponse.class);
422+
// Extract error message from NotFoundError('message') format
423+
Matcher notFoundErrorMatcher = NOT_FOUND_ERROR_PATTERN.matcher(errorResponse.error());
424+
if (notFoundErrorMatcher.find()) {
425+
return notFoundErrorMatcher.group(1);
426+
}
427+
// Return the error as-is if it doesn't match NotFoundError pattern
428+
return errorResponse.error();
429+
}
430+
catch (JsonProcessingException ex) {
431+
// If JSON parsing fails, return the response body as-is if it's not empty
432+
if (StringUtils.hasText(responseBody.trim())) {
433+
return responseBody.trim();
434+
}
435+
logger.debug("Failed to parse error response as JSON, falling back to exception message", ex);
436+
}
437+
}
438+
330439
var errorMessage = e.getMessage();
331440

332441
// If the error message is empty or null, return an empty string
@@ -622,6 +731,15 @@ private static class CollectionList extends ArrayList<Collection> {
622731

623732
}
624733

734+
/**
735+
* Chroma API error response.
736+
*
737+
* @param error The error message from Chroma API.
738+
*/
739+
@JsonInclude(JsonInclude.Include.NON_NULL)
740+
private record ErrorResponse(@JsonProperty("error") String error) {
741+
}
742+
625743
public static final class Builder {
626744

627745
private String baseUrl = ChromaApiConstants.DEFAULT_BASE_URL;

0 commit comments

Comments
 (0)