projectdiscovery/nuclei · error
failed to build descriptor source from protoset: %w
Error message
failed to build descriptor source from protoset: %w
What it means
grpcurl.DescriptorSourceFromFileDescriptorSet rejected the parsed FileDescriptorSet: typically the set does not contain the service/method the template will invoke, or required imported types are missing, so symbol resolution against the set cannot be built and later ListServices/Invoke calls would fail.
Source
Thrown at pkg/js/libs/grpc/invoke.go:114
noop := func() {}
if strings.TrimSpace(protosetFile) != "" {
// resolve through the local-file-access allowlist: unless -lfa is set,
// only files inside the nuclei-templates directory are permitted.
normalized, err := protocolstate.NormalizePathWithExecutionId(executionID, protosetFile)
if err != nil {
return nil, noop, fmt.Errorf("protoset path denied: %w", err)
}
data, err := os.ReadFile(normalized)
if err != nil {
return nil, noop, fmt.Errorf("failed to read protoset file: %w", err)
}
fds := &descriptorpb.FileDescriptorSet{}
if err := proto.Unmarshal(data, fds); err != nil {
return nil, noop, fmt.Errorf("failed to parse protoset file: %w", err)
}
src, err := grpcurl.DescriptorSourceFromFileDescriptorSet(fds)
if err != nil {
return nil, noop, fmt.Errorf("failed to build descriptor source from protoset: %w", err)
}
return src, noop, nil
}
refClient := grpcreflect.NewClientAuto(ctx, cc)
cleanup := func() { refClient.Reset() }
return grpcurl.DescriptorSourceFromServer(ctx, refClient), cleanup, nil
}
// invokeUnary invokes a unary (or single-response) gRPC method described by src
// over cc, marshaling the JSON request and formatting the JSON response.
func invokeUnary(ctx context.Context, src grpcurl.DescriptorSource, cc *grpc.ClientConn, method, requestJSON string, headers []string) (string, error) {
body := strings.TrimSpace(requestJSON)
if body == "" {
body = "{}"
}
var in io.Reader = strings.NewReader(body)
View on GitHub (pinned to 265b3a3dec)
Solutions
- Recompile including imports and every relevant schema: protoc --descriptor_set_out=acme.protoset --include_imports --proto_path=. acme/*.proto
- Verify coverage: after Connect(), call ListServices() and check the fully-qualified service name matches the Invoke method string
- If the target supports reflection, drop ProtosetFile and let the server provide its own schema
Example fix
# before: descriptor compiled without imports -> symbols unresolvable protoc --descriptor_set_out=acme.protoset acme.proto # after: include imports (and all files that define the service) protoc --descriptor_set_out=acme.protoset --include_imports --proto_path=. acme.proto
Defensive patterns
Strategy: try-catch
Try / catch
try {
const c = new grpc.Client(t, o);
c.Connect();
const svc = c.ListServices(); // verify the service you plan to Invoke is present
} catch (e) {
if (/descriptor source/.test(e.message || '')) {
// recompile protoset with --include_imports, or omit ProtosetFile to use server reflection
}
} Prevention
- Always compile protosets with --include_imports and all relevant .proto files
- Cross-check the fully-qualified service name via ListServices before Invoke
- Keep protoset versions in sync with the deployed service schema
When it happens
Trigger: Descriptor compiled without --include_imports so transitive message types are absent; invoking 'acme.v1.Svc/Get' while the protoset only defines acme.v2; merging multiple .proto files into one set with conflicting duplicate symbols.
Common situations: Protoset generated from a subset of the repo's .proto files; version drift between the descriptor the author compiled and the service actually deployed; renaming services between proto versions.
Related errors
- failed to parse protoset file: %w
- can't get example for array item: %+v
- can't get example for '%s': %+v
- can't get example for additional properties: %+v
- invalid grpc target %q (expected host:port): %w
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/a947e434601ccf6a.
Report an issue: GitHub.