Tencent/WeKnora · error
gRPC ReadStream returned no metadata frame
Error message
gRPC ReadStream returned no metadata frame
What it means
This error is returned by GRPCDocumentReader.readStream after consuming the server-streaming ReadStream RPC to completion (io.EOF) without ever receiving a frame whose meta field is populated. The protocol contract is one metadata frame first, followed by one frame per image; if the stream ends with gotMeta still false, the client cannot build a valid ReadResult and fails closed rather than returning an empty result.
Source
Thrown at internal/infrastructure/docparser/grpc_parser.go:195
if n := meta.GetImageCount(); n > 0 {
result.ImageRefs = make([]types.ImageRef, 0, n)
}
continue
}
if img := frame.GetImage(); img != nil {
result.ImageRefs = append(result.ImageRefs, types.ImageRef{
Filename: img.GetFilename(),
OriginalRef: img.GetOriginalRef(),
MimeType: img.GetMimeType(),
StorageKey: img.GetStorageKey(),
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(),View on GitHub (pinned to 988cbb0330)
Solutions
- Check the docreader server version and logs — upgrade/align the docreader deployment with the client so the ReadStream implementation sends the meta frame first
- Inspect the specific document: try parsing a small, well-formed file to see if the missing meta frame is document-specific (server-side parse failure returning an empty stream)
- Confirm no intermediate proxy/LB is cutting the stream short — test the docreader service directly with grpcurl or a minimal client
- Check result.Error handling on the server: ensure server-side parse errors are conveyed via the meta frame's error field rather than closing the stream silently
- As a last resort, retry the Read; if it reproduces consistently, capture a gRPC trace of the stream frames
Example fix
// before
result, err := reader.Read(ctx, req)
if err != nil {
return err // opaque 'no metadata frame' failure
}
// after
result, err := reader.Read(ctx, req)
if err != nil {
if strings.Contains(err.Error(), "returned no metadata frame") {
logger.Warnf(ctx, "docreader sent empty/malformed stream for %s, check server version", req.FileName)
}
return err
} Defensive patterns
Strategy: validation
Validate before calling
if !reader.IsConnected() {
return fmt.Errorf("docreader not connected; cannot read %s", req.FileName)
}
// Optionally probe server capability once at startup so version-skew is
detected before document reads:
_, err := reader.ListEngines(ctx, nil)
if err != nil {
return fmt.Errorf("docreader capability probe failed: %w", err)
} Try / catch
result, err := reader.Read(ctx, req)
if err != nil {
if strings.Contains(err.Error(), "returned no metadata frame") {
// malformed/empty stream: retry once, then surface a document-specific error
if result, rerr := reader.Read(ctx, req); rerr == nil {
return result, nil
}
return nil, fmt.Errorf("docreader returned malformed stream for %s (check server version)", req.FileName)
}
return nil, err
} Prevention
- Keep docreader server and client versions aligned so ReadStream framing always includes the meta frame first
- Check result.Error from the meta frame path rather than letting the server close streams silently on parse failure
- Avoid proxies/LBs that can truncate long-lived gRPC streams; configure generous idle timeouts
- Test with a small known-good document after every docreader upgrade
When it happens
Trigger: Calling Read on a GRPCDocumentReader where the docreader server closes the ReadStream stream (sends only io.EOF) without ever sending a meta frame — e.g. a server that sends only image frames, a silently dropped/empty stream, or a server-side bug that skips the meta frame on certain documents.
Common situations: Version-skewed deployments where the docreader side implements ReadStream but with different framing semantics (e.g. older/patched builds that omit the meta frame when there is no content); a proxy or load balancer terminating the stream after connect; a server crash mid-handshake after opening the stream but before sending the first frame.
Related errors
- gRPC ListEngines failed: %w
- create qdrant client: %w
- create milvus client: %w
- QA pipeline error: %s
- anydoc scanned-PDF fallback returned no result for %q
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/e958d70ec246ee58.
Report an issue: GitHub.