You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建含重复字段的gRPC客户端请求时触发无效内存地址错误排查

问题:gRPC服务端空指针解引用错误(重复字段请求场景)

错误信息

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x165cf0e]

goroutine 40 [running]:
main.(*Server).RunCommands(0x172b960?, {0x18be540?, 0xc00031c030?}, 0x107a360?)
        <autogenerated>:1 +0x2e
github.com/mygithub/some-package/proto._ExecuterService_RunCommands_Handler({0x1701a20?, 0xc0002430d0}, {0x18be540, 0xc00031c030}, 0xc000318070, 0x0)
        ..../executer_grpc.pb.go:93 +0x170

对应executer_grpc.pb.go第93行代码:

in := new(CommandsRequest)
return srv.(ExecuterServiceServer).RunCommands(ctx, in)

相关代码

服务端代码(grpc_server.go)

package main

import (
    "fmt"
    pb "github.com/mygithub/some-package/proto"
    "google.golang.org/grpc"
)


type Server struct {
    pb.ExecuterServiceServer
}

func startServer() {
    addr := GetAgentAddress()
    lis, err := net.Listen("tcp", addr)

    if err != nil {
        log.Fatalf("Faild to listen on %v\n", err)
    }
    s := grpc.NewServer()
    pb.RegisterExecuterServiceServer(s, &Server{})

    if err = s.Serve(lis); err != nil {
        log.Fatalf("Failed to serve: %v\n", err)
    }

}

客户端代码(grpc_client.go)

package main

import (
    "context"
    "encoding/json"
    "fmt"
    v1 "github.com/mygithub/some-package/api/v1"
    pb "github.com/mygithub/some-package/proto"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

func sendCommandsWithClient() (string, error) {
    command := v1.CommandSpec{
        BashCommand: "ls",
        Async:       false,
    }

    addr := "localhost:4000"
    conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        return "", err
    }

    defer conn.Close()
    client := pb.NewExecuterServiceClient(conn)
    if err != nil {
        return "", err
    }

    commandRequest := &pb.CommandRequest{
        BashCommand: command.BashCommand,
        Slug:        "test",
        Async:       command.Async,
    }

    req := &pb.CommandsRequest{}
    req.Commands = append(req.Commands, commandRequest)
    aAsJsonString, _ := json.Marshal(req)
    fmt.Println(string(aAsJsonString))
    res, err := client.RunCommands(context.Background(), req)

    if err != nil {
        return "", err
    }
    return res.Content, nil
}

func main() {
    _, err := sendCommandsWithClient()
    if err != nil {
        return
    }
}

RunCommands方法实现

func (s *Server) RunCommands(ctx context.Context, in *pb.CommandsRequest) (*pb.CommandsResponse, error) {
    log.Println("test")
    command := v1.CommandSpec{BashCommand: "ls", Slug: "test", Async: true}
    CommandsQueue := append(CommandsQueue, command)
    size := len(CommandsQueue)
    return &pb.CommandsResponse{Status: 0, Content: strconv.Itoa(size)}, nil
}

问题根源与解决方案

问题根源

服务端的Server结构体错误嵌入了pb.ExecuterServiceServer接口:

type Server struct {
    pb.ExecuterServiceServer
}

初始化&Server{}时,嵌入的接口字段默认是nil。gRPC框架调用方法时会优先尝试调用该嵌入接口的RunCommands方法,因接口实例为nil,直接触发空指针解引用错误。

实际上你的Server结构体已经通过自定义的RunCommands方法实现了pb.ExecuterServiceServer接口,完全不需要嵌入该接口。

解决方案

修改服务端的Server结构体定义,移除嵌入的接口:

type Server struct {
    // 可添加自定义业务字段,无需嵌入ExecuterServiceServer接口
}

修改后,pb.RegisterExecuterServiceServer(s, &Server{})会正确识别Server结构体实现了接口方法,调用时直接执行你自定义的RunCommands函数,不会再触发空指针错误。

客户端代码无问题:req.Commands = append(req.Commands, commandRequest)可正常处理nil切片(proto3重复字段默认初始化为nil,append会自动创建新切片)。

环境信息:go1.18.2 darwin/amd64,protoc proto3


内容的提问来源于stack exchange,提问作者DevFromI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 16:01:11