amir20/dozzle · error

unknown error: with

Error message

unknown error: %v with %w

What it means

rpcErrToErr maps gRPC status code Unknown (with a message other than "EOF") to "unknown error: %v with %w", wrapping the original status error. This is the catch-all for server-side errors returned without a specific gRPC code, usually panics or plain Go errors from the agent handler.

Solutions

  1. Read the wrapped status message (`status.Convert(err).Message()`) to find the root cause, and check agent container logs at the same time
  2. Fix the underlying agent-side condition (e.g. mount /var/run/docker.sock with correct permissions in the agent container)
  3. Ensure agent and server run the same Dozzle version to avoid protocol mismatches
  4. File/report a panic if the message contains a stack trace; unclassified errors should map to specific codes

Example fix

// before
if err := <-errCh; err != nil { log.Error().Msg("agent call failed") }
// after
if err := <-errCh; err != nil {
    if st, ok := status.FromError(errors.Unwrap(err)); ok {
        log.Error().Str("grpc_msg", st.Message()).Msg("agent call failed")
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

if err != nil {
    if st, ok := status.FromError(errors.Unwrap(err)); ok {
        log.Error().Str("code", st.Code().String()).Str("msg", st.Message()).Msg("agent error")
    }
}

Prevention

When it happens

Trigger: Any agent RPC whose server handler returned an unclassified error (a plain Go error via status.Unknown or a panic recovered by gRPC), e.g. Docker API failures inside the agent.

Common situations: Agent failing to reach the local Docker socket (permission denied on /var/run/docker.sock); Docker API errors while listing containers or reading logs; version mismatch causing unhandled proto fields; agent panics on unexpected input.

Related errors


AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/7b7eae9c4475024f. Report an issue: GitHub.

Appendix: source

Thrown at internal/agent/client.go:127

		return nil
	}

	status, ok := status.FromError(err)
	if !ok {
		return err
	}

	if status.Code() == codes.Unknown && status.Message() == "EOF" {
		return fmt.Errorf("EOF error while converting gRPC to error: %w", io.EOF)
	}

	switch status.Code() {
	case codes.Canceled:
		return fmt.Errorf("canceled: %v with %w", status.Message(), context.Canceled)
	case codes.DeadlineExceeded:
		return fmt.Errorf("deadline exceeded: %v with %w", status.Message(), context.DeadlineExceeded)
	case codes.Unknown:
		return fmt.Errorf("unknown error: %v with %w", status.Message(), err)
	case codes.OK:
		return nil
	default:
		return fmt.Errorf("unknown code: %v with %w", status.Code(), err)
	}
}

func (c *Client) LogsBetweenDates(ctx context.Context, containerID string, since time.Time, until time.Time, std container.StdType) (<-chan *container.LogEvent, error) {
	stream, err := c.client.LogsBetweenDates(ctx, &pb.LogsBetweenDatesRequest{
		ContainerId: containerID,
		Since:       timestamppb.New(since),
		Until:       timestamppb.New(until),
		StreamTypes: int32(std),
	})

	if err != nil {
		return nil, err
	}

View on GitHub (pinned to d9463cbe21)