Tencent/WeKnora · error
gRPC Read failed: %w
Error message
gRPC Read failed: %w
What it means
readUnary wraps any error from the legacy unary Read RPC with 'gRPC Read failed: %w'. This path is only reached as a compatibility fallback when the connected docreader reports codes.Unimplemented for ReadStream, so the wrapped error is always an underlying unary RPC failure (transport error, gRPC status like Unavailable/DeadlineExceeded/ResourceExhausted, or an older server rejecting the request).
Source
Thrown at internal/infrastructure/docparser/grpc_parser.go:207
ImageData: img.GetImageData(),
})
}
}
if !gotMeta {
return nil, fmt.Errorf("gRPC ReadStream returned no metadata frame")
}
return result, nil
}
// readUnary calls the legacy unary Read RPC. Used only as a compatibility
// fallback when the connected docreader does not implement ReadStream.
func (p *GRPCDocumentReader) readUnary(
ctx context.Context, client proto.DocReaderClient, protoReq *proto.ReadRequest,
) (*types.ReadResult, error) {
resp, err := client.Read(ctx, protoReq)
if err != nil {
return nil, fmt.Errorf("gRPC Read failed: %w", err)
}
result := &types.ReadResult{
MarkdownContent: resp.GetMarkdownContent(),
ImageDirPath: resp.GetImageDirPath(),
Metadata: resp.GetMetadata(),
Error: resp.GetError(),
}
if refs := resp.GetImageRefs(); len(refs) > 0 {
result.ImageRefs = make([]types.ImageRef, 0, len(refs))
for _, img := range refs {
result.ImageRefs = append(result.ImageRefs, types.ImageRef{
Filename: img.GetFilename(),
OriginalRef: img.GetOriginalRef(),
MimeType: img.GetMimeType(),
StorageKey: img.GetStorageKey(),
ImageData: img.GetImageData(),
})View on GitHub (pinned to 988cbb0330)
Solutions
- Unwrap the error with errors.Unwrap or status.Convert(err) to see the real gRPC code and fix that root cause first
- Check docreader availability (kubectl get pods / service endpoint) and network reachability from the client
- If the error is ResourceExhausted, raise MAX_FILE_SIZE_MB on both sides or upgrade the docreader so streaming (ReadStream) is used instead of the unary fallback
- Upgrade the docreader build to one that implements ReadStream so the unary fallback path is not used at all
- Verify TLS/auth env config used by docclient.LoadAuthConfigFromEnv matches the server's expectations
Example fix
// before
result, err := reader.Read(ctx, req)
if err != nil {
return fmt.Errorf("parse failed: %w", err)
}
// after
result, err := reader.Read(ctx, req)
if err != nil {
if st, ok := status.FromError(errors.Unwrap(err)); ok && st.Code() == codes.ResourceExhausted {
return fmt.Errorf("document too large for legacy unary path (max %d MB); upgrade docreader to enable streaming", maxSizeMB)
}
return fmt.Errorf("parse failed: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
if !reader.IsConnected() {
return fmt.Errorf("docreader gRPC client not connected")
}
// Keep documents under the unary limit since the fallback path is size-capped:
const maxUnaryBytes = 50 * 1024 * 1024 // or read MAX_FILE_SIZE_MB
if len(req.FileContent) > maxUnaryBytes {
return fmt.Errorf("document %s exceeds unary fallback limit (%d bytes); streaming docreader required", req.FileName, maxUnaryBytes)
} Try / catch
result, err := reader.Read(ctx, req)
if err != nil {
var inner error = err
for errors.Unwrap(inner) != nil { inner = errors.Unwrap(inner) }
if st, ok := status.FromError(inner); ok {
switch st.Code() {
case codes.ResourceExhausted:
return nil, fmt.Errorf("document too large for legacy unary path: %w", err)
case codes.Unavailable:
return nil, fmt.Errorf("docreader unavailable: %w", err) // retry with backoff
default:
return nil, fmt.Errorf("docreader read failed (%s): %w", st.Code(), err)
}
}
return nil, err
} Prevention
- Deploy a docreader build that implements ReadStream so the size-capped unary fallback is never used
- Set MAX_FILE_SIZE_MB consistently on client and server
- Use per-request contexts with deadlines sized for large documents
- Monitor gRPC status codes (Unavailable, ResourceExhausted) and alert on them
When it happens
Trigger: Read is called, ReadStream returns codes.Unimplemented (docreader build predates streaming), the client falls back to readUnary, and then client.Read fails — e.g. server down, context deadline exceeded, message larger than the unary max size (MAX_FILE_SIZE_MB / 50MB default), or auth/TLS rejection.
Common situations: Large scanned PDFs exceeding the unary message-size limit (the very limit streaming was introduced to avoid); docreader pod restarting or unreachable; version-skewed deployment forcing the legacy path; expired auth token or misconfigured TLS on the gRPC channel; per-request context timeout too short for big documents.
Related errors
- gRPC ListEngines failed: %w
- create qdrant client: %w
- create milvus client: %w
- gRPC ReadStream returned no metadata frame
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/bc71fbac6693f28f.
Report an issue: GitHub.