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

  1. Check the docreader server version and logs — upgrade/align the docreader deployment with the client so the ReadStream implementation sends the meta frame first
  2. 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)
  3. Confirm no intermediate proxy/LB is cutting the stream short — test the docreader service directly with grpcurl or a minimal client
  4. 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
  5. 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

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


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/e958d70ec246ee58. Report an issue: GitHub.