CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-dev-langchain4j--langchain4j-qdrant

LangChain4j Qdrant integration providing a vector store embedding implementation for Qdrant database with metadata filtering support

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Complete guide to exception handling and error recovery in QdrantEmbeddingStore.

Exception Types

RuntimeException

Thrown for GRPC operation failures during communication with Qdrant.

Causes:

  • Network connectivity issues
  • Qdrant server errors
  • Timeout issues
  • GRPC communication failures

Operations that throw:

  • add() and addAll()
  • search()
  • remove() and removeAll()
  • clearStore()

Example:

try {
    store.add(embedding, textSegment);
} catch (RuntimeException e) {
    System.err.println("Failed to add embedding: " + e.getMessage());
    // Log error, retry, or handle gracefully
}

IllegalArgumentException

Thrown for invalid method parameters.

Causes:

  • Null or blank ID in remove(String id)
  • Invalid parameter values

Example:

try {
    store.remove("");  // Empty string
} catch (IllegalArgumentException e) {
    System.err.println("Invalid ID: " + e.getMessage());
}

try {
    store.remove(null);  // Null value
} catch (IllegalArgumentException e) {
    System.err.println("ID cannot be null: " + e.getMessage());
}

NullPointerException

Thrown by the builder when required parameters are missing.

Causes:

  • Missing collectionName in builder

Example:

try {
    QdrantEmbeddingStore store = QdrantEmbeddingStore.builder()
        .host("localhost")
        .build();  // Missing collectionName
} catch (NullPointerException e) {
    System.err.println("Missing required configuration: " + e.getMessage());
}

UnsupportedOperationException

Thrown for unsupported filter types or value types.

Causes:

  • Unsupported filter implementations during filter conversion
  • Unsupported metadata value types (e.g., Boolean, Arrays)
  • Unsupported Qdrant payload types during conversion

Example:

import dev.langchain4j.data.document.Metadata;

try {
    Metadata metadata = new Metadata();
    metadata.put("flag", true);  // Boolean not supported
    TextSegment segment = TextSegment.from("Text", metadata);
    store.add(embedding, segment);
} catch (UnsupportedOperationException e) {
    System.err.println("Unsupported value type: " + e.getMessage());
}

Error Handling Patterns

Basic Try-Catch

import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;

try {
    Embedding embedding = Embedding.from(new float[]{0.1f, 0.2f, 0.3f});
    TextSegment segment = TextSegment.from("Sample text");
    String id = store.add(embedding, segment);
    System.out.println("Successfully added with ID: " + id);
} catch (RuntimeException e) {
    System.err.println("Error adding embedding: " + e.getMessage());
    e.printStackTrace();
}

Retry with Backoff

public String addWithRetry(Embedding embedding, TextSegment segment, int maxAttempts) {
    int attempt = 0;
    long waitTime = 1000; // Start with 1 second

    while (attempt < maxAttempts) {
        try {
            return store.add(embedding, segment);
        } catch (RuntimeException e) {
            attempt++;
            if (attempt >= maxAttempts) {
                throw new RuntimeException("Failed after " + maxAttempts + " attempts", e);
            }

            System.err.println("Attempt " + attempt + " failed, retrying in " + waitTime + "ms");
            try {
                Thread.sleep(waitTime);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Interrupted during retry", ie);
            }

            waitTime *= 2; // Exponential backoff
        }
    }

    throw new RuntimeException("Should not reach here");
}

Graceful Degradation

public List<EmbeddingMatch<TextSegment>> searchWithFallback(
    Embedding query,
    Filter primaryFilter,
    Filter fallbackFilter
) {
    try {
        // Try with primary filter
        EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
            .queryEmbedding(query)
            .maxResults(10)
            .filter(primaryFilter)
            .build();

        EmbeddingSearchResult<TextSegment> results = store.search(request);
        return results.matches();

    } catch (RuntimeException e) {
        System.err.println("Primary search failed, trying fallback: " + e.getMessage());

        try {
            // Fallback to simpler filter
            EmbeddingSearchRequest fallbackRequest = EmbeddingSearchRequest.builder()
                .queryEmbedding(query)
                .maxResults(10)
                .filter(fallbackFilter)
                .build();

            EmbeddingSearchResult<TextSegment> results = store.search(fallbackRequest);
            return results.matches();

        } catch (RuntimeException e2) {
            System.err.println("Fallback search also failed: " + e2.getMessage());
            return Collections.emptyList();
        }
    }
}

Bulk Operation Error Handling

import java.util.ArrayList;
import java.util.List;

public List<String> addAllWithErrorHandling(
    List<Embedding> embeddings,
    List<TextSegment> segments
) {
    List<String> successfulIds = new ArrayList<>();

    // Try bulk operation first
    try {
        return store.addAll(embeddings);
    } catch (RuntimeException e) {
        System.err.println("Bulk operation failed, falling back to individual inserts");
    }

    // Fallback to individual operations
    for (int i = 0; i < embeddings.size(); i++) {
        try {
            String id = store.add(
                embeddings.get(i),
                segments != null && i < segments.size() ? segments.get(i) : null
            );
            successfulIds.add(id);
        } catch (RuntimeException e) {
            System.err.println("Failed to add embedding at index " + i + ": " + e.getMessage());
            // Continue with next embedding
        }
    }

    return successfulIds;
}

Validation Patterns

Pre-validation

public void addWithValidation(Embedding embedding, TextSegment segment) {
    // Validate embedding
    if (embedding == null) {
        throw new IllegalArgumentException("Embedding cannot be null");
    }
    if (embedding.vector() == null || embedding.vector().length == 0) {
        throw new IllegalArgumentException("Embedding vector cannot be empty");
    }

    // Validate text segment
    if (segment != null && segment.metadata() != null) {
        validateMetadata(segment.metadata());
    }

    // Perform operation
    try {
        store.add(embedding, segment);
    } catch (RuntimeException e) {
        System.err.println("Failed to add after validation: " + e.getMessage());
        throw e;
    }
}

private void validateMetadata(Metadata metadata) {
    for (String key : metadata.asMap().keySet()) {
        Object value = metadata.get(key);

        if (value instanceof Boolean) {
            throw new IllegalArgumentException(
                "Boolean values not supported. Use Integer (0/1) for key: " + key
            );
        }
        if (value instanceof List || value instanceof Map) {
            throw new IllegalArgumentException(
                "Collection types not supported for key: " + key
            );
        }
    }
}

ID Validation

import java.util.UUID;

public void removeWithValidation(String id) {
    // Check null or empty
    if (id == null || id.trim().isEmpty()) {
        throw new IllegalArgumentException("ID cannot be null or empty");
    }

    // Validate UUID format
    try {
        UUID.fromString(id);
    } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException("ID must be valid UUID format: " + id, e);
    }

    // Perform removal
    try {
        store.remove(id);
    } catch (RuntimeException e) {
        System.err.println("Failed to remove ID " + id + ": " + e.getMessage());
        throw e;
    }
}

Connection Error Handling

Connection Retry

public QdrantEmbeddingStore createStoreWithRetry(int maxAttempts) {
    int attempt = 0;
    long waitTime = 1000;

    while (attempt < maxAttempts) {
        try {
            QdrantEmbeddingStore store = QdrantEmbeddingStore.builder()
                .collectionName("my-collection")
                .build();

            // Test connection
            Embedding testEmbedding = Embedding.from(new float[]{1.0f});
            String id = store.add(testEmbedding);
            store.remove(id);

            System.out.println("Connection successful");
            return store;

        } catch (RuntimeException e) {
            attempt++;
            if (attempt >= maxAttempts) {
                throw new RuntimeException(
                    "Failed to connect after " + maxAttempts + " attempts", e
                );
            }

            System.err.println("Connection attempt " + attempt + " failed, retrying...");
            try {
                Thread.sleep(waitTime);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Interrupted during connection retry", ie);
            }

            waitTime *= 2;
        }
    }

    throw new RuntimeException("Should not reach here");
}

Health Check

public boolean isStoreHealthy(QdrantEmbeddingStore store) {
    try {
        // Perform simple operation to test connection
        Embedding testEmbedding = Embedding.from(new float[]{1.0f, 0.0f});
        String id = store.add(testEmbedding);
        store.remove(id);
        return true;
    } catch (RuntimeException e) {
        System.err.println("Health check failed: " + e.getMessage());
        return false;
    }
}

Search Error Handling

Safe Search

import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import java.util.Collections;
import java.util.List;

public List<EmbeddingMatch<TextSegment>> safeSearch(
    Embedding query,
    int maxResults,
    double minScore
) {
    try {
        EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
            .queryEmbedding(query)
            .maxResults(maxResults)
            .minScore(minScore)
            .build();

        EmbeddingSearchResult<TextSegment> results = store.search(request);
        return results.matches();

    } catch (RuntimeException e) {
        System.err.println("Search failed: " + e.getMessage());
        return Collections.emptyList();
    }
}

Filter Error Recovery

public List<EmbeddingMatch<TextSegment>> searchWithFilterRecovery(
    Embedding query,
    Filter filter
) {
    try {
        // Try with filter
        EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
            .queryEmbedding(query)
            .maxResults(10)
            .filter(filter)
            .build();

        return store.search(request).matches();

    } catch (UnsupportedOperationException e) {
        System.err.println("Filter not supported: " + e.getMessage());

        // Retry without filter
        EmbeddingSearchRequest simpleRequest = EmbeddingSearchRequest.builder()
            .queryEmbedding(query)
            .maxResults(10)
            .build();

        return store.search(simpleRequest).matches();

    } catch (RuntimeException e) {
        System.err.println("Search failed: " + e.getMessage());
        return Collections.emptyList();
    }
}

Resource Management

Proper Cleanup

public void processWithCleanup() {
    QdrantEmbeddingStore store = null;
    try {
        store = QdrantEmbeddingStore.builder()
            .collectionName("my-collection")
            .build();

        // Use store...
        store.add(embedding, segment);

    } catch (RuntimeException e) {
        System.err.println("Error during processing: " + e.getMessage());
        throw e;

    } finally {
        if (store != null) {
            try {
                store.close();
            } catch (Exception e) {
                System.err.println("Error closing store: " + e.getMessage());
            }
        }
    }
}

Store Wrapper with Auto-Close

public class ManagedQdrantStore implements AutoCloseable {
    private final QdrantEmbeddingStore store;

    public ManagedQdrantStore(String collectionName) {
        this.store = QdrantEmbeddingStore.builder()
            .collectionName(collectionName)
            .build();
    }

    public String add(Embedding embedding, TextSegment segment) {
        return store.add(embedding, segment);
    }

    public EmbeddingSearchResult<TextSegment> search(EmbeddingSearchRequest request) {
        return store.search(request);
    }

    @Override
    public void close() {
        try {
            store.close();
        } catch (Exception e) {
            System.err.println("Error closing store: " + e.getMessage());
        }
    }
}

// Usage
try (ManagedQdrantStore store = new ManagedQdrantStore("my-collection")) {
    store.add(embedding, segment);
    // Store automatically closed
}

Logging Best Practices

Structured Logging

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class QdrantStoreService {
    private static final Logger logger = LoggerFactory.getLogger(QdrantStoreService.class);
    private final QdrantEmbeddingStore store;

    public String addWithLogging(Embedding embedding, TextSegment segment) {
        try {
            logger.debug("Adding embedding to store");
            String id = store.add(embedding, segment);
            logger.info("Successfully added embedding with ID: {}", id);
            return id;

        } catch (RuntimeException e) {
            logger.error("Failed to add embedding: {}", e.getMessage(), e);
            throw e;
        }
    }

    public List<EmbeddingMatch<TextSegment>> searchWithLogging(
        Embedding query,
        Filter filter
    ) {
        try {
            logger.debug("Searching with filter: {}", filter);
            EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
                .queryEmbedding(query)
                .maxResults(10)
                .filter(filter)
                .build();

            EmbeddingSearchResult<TextSegment> results = store.search(request);
            logger.info("Found {} matches", results.matches().size());
            return results.matches();

        } catch (RuntimeException e) {
            logger.error("Search failed: {}", e.getMessage(), e);
            return Collections.emptyList();
        }
    }
}

Error Recovery Strategies

Circuit Breaker Pattern

public class CircuitBreakerStore {
    private final QdrantEmbeddingStore store;
    private int failureCount = 0;
    private long lastFailureTime = 0;
    private static final int FAILURE_THRESHOLD = 5;
    private static final long RESET_TIMEOUT = 60000; // 1 minute

    public CircuitBreakerStore(QdrantEmbeddingStore store) {
        this.store = store;
    }

    public String add(Embedding embedding, TextSegment segment) {
        if (isCircuitOpen()) {
            throw new RuntimeException("Circuit breaker is open");
        }

        try {
            String id = store.add(embedding, segment);
            onSuccess();
            return id;

        } catch (RuntimeException e) {
            onFailure();
            throw e;
        }
    }

    private boolean isCircuitOpen() {
        if (failureCount >= FAILURE_THRESHOLD) {
            long timeSinceLastFailure = System.currentTimeMillis() - lastFailureTime;
            if (timeSinceLastFailure > RESET_TIMEOUT) {
                reset();
                return false;
            }
            return true;
        }
        return false;
    }

    private void onSuccess() {
        failureCount = 0;
    }

    private void onFailure() {
        failureCount++;
        lastFailureTime = System.currentTimeMillis();
    }

    private void reset() {
        failureCount = 0;
        lastFailureTime = 0;
    }
}

Common Error Scenarios

Collection Not Found

Error: Collection 'my-collection' not found

Solution:

import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.Distance;

public QdrantEmbeddingStore createOrGetStore(String collectionName, int vectorSize) {
    QdrantClient client = new QdrantClient(
        QdrantGrpcClient.newBuilder("localhost", 6334, false).build()
    );

    try {
        // Try to get collection info
        client.getCollectionInfoAsync(collectionName).get();
        System.out.println("Collection exists");

    } catch (Exception e) {
        // Collection doesn't exist, create it
        System.out.println("Creating collection: " + collectionName);
        try {
            client.createCollectionAsync(
                collectionName,
                VectorParams.newBuilder()
                    .setDistance(Distance.Cosine)
                    .setSize(vectorSize)
                    .build()
            ).get();
        } catch (Exception createError) {
            throw new RuntimeException("Failed to create collection", createError);
        }
    } finally {
        client.close();
    }

    return QdrantEmbeddingStore.builder()
        .collectionName(collectionName)
        .build();
}

Dimension Mismatch

Error: Vector dimension mismatch

Solution:

public void addWithDimensionCheck(
    Embedding embedding,
    TextSegment segment,
    int expectedDimension
) {
    if (embedding.vector().length != expectedDimension) {
        throw new IllegalArgumentException(
            String.format(
                "Dimension mismatch: expected %d, got %d",
                expectedDimension,
                embedding.vector().length
            )
        );
    }

    try {
        store.add(embedding, segment);
    } catch (RuntimeException e) {
        System.err.println("Failed to add embedding: " + e.getMessage());
        throw e;
    }
}

Timeout Errors

Solution:

import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import java.time.Duration;

public QdrantEmbeddingStore createStoreWithTimeout(String collectionName) {
    QdrantGrpcClient grpcClient = QdrantGrpcClient.newBuilder(
            "localhost",
            6334,
            false
        )
        .withDeadline(Duration.ofSeconds(30))  // Set timeout
        .build();

    QdrantClient client = new QdrantClient(grpcClient);

    return QdrantEmbeddingStore.builder()
        .client(client)
        .collectionName(collectionName)
        .build();
}

Testing Error Conditions

Mock Error Scenarios

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

@Test
public void testAddWithConnectionFailure() {
    // Test with invalid host
    QdrantEmbeddingStore store = QdrantEmbeddingStore.builder()
        .host("invalid-host")
        .collectionName("test-collection")
        .build();

    Embedding embedding = Embedding.from(new float[]{1.0f, 0.0f});

    assertThrows(RuntimeException.class, () -> {
        store.add(embedding);
    });
}

@Test
public void testRemoveWithInvalidId() {
    QdrantEmbeddingStore store = QdrantEmbeddingStore.builder()
        .collectionName("test-collection")
        .build();

    assertThrows(IllegalArgumentException.class, () -> {
        store.remove("");
    });

    assertThrows(IllegalArgumentException.class, () -> {
        store.remove(null);
    });
}

@Test
public void testBuilderWithMissingCollection() {
    assertThrows(NullPointerException.class, () -> {
        QdrantEmbeddingStore.builder().build();
    });
}

Install with Tessl CLI

npx tessl i tessl/maven-dev-langchain4j--langchain4j-qdrant@1.11.0

docs

api.md

error-handling.md

examples.md

filters.md

index.md

setup.md

tile.json