projectdiscovery/nuclei · error
failed to parse protoset file: %w
Error message
failed to parse protoset file: %w
What it means
The file was read successfully but proto.Unmarshal could not decode it as a protobuf FileDescriptorSet. ProtosetFile must point to a compiled binary descriptor set, not to .proto source text, a JSON descriptor, or any other encoding — the unmarshaler rejects those immediately.
Source
Thrown at pkg/js/libs/grpc/invoke.go:110
// compiled protoset file (read through the local-file-access allowlist) or, when
// no protoset is provided, from server reflection over the existing connection.
// The returned cleanup func must be called once the source is no longer needed.
func descriptorSource(ctx context.Context, executionID string, cc *grpc.ClientConn, protosetFile string) (grpcurl.DescriptorSource, func(), error) {
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 == "" {View on GitHub (pinned to 265b3a3dec)
Solutions
- Compile the schema to a real descriptor set: protoc --descriptor_set_out=acme.protoset --include_imports acme.proto
- Sanity-check the artifact: a valid protoset is binary and non-empty, it does not start with 'syntax ='
- If you only have .proto source and the target supports reflection, omit ProtosetFile and let the schema come from the server
Example fix
// before: proto source, not a compiled descriptor set o.ProtosetFile = 'acme.proto'; // -> failed to parse protoset file // after: compile first, then reference the binary descriptor // shell: protoc --descriptor_set_out=acme.protoset --include_imports acme.proto o.ProtosetFile = 'acme.protoset';
Defensive patterns
Strategy: validation
Validate before calling
if (!o.ProtosetFile.endsWith('.protoset')) {
// almost certainly raw .proto source: compile it first with
// protoc --descriptor_set_out=... --include_imports
} Type guard
const looksLikeDescriptorSet = (path) => path.endsWith('.protoset'); Try / catch
try { const c = new grpc.Client(t, o); c.Connect(); }
catch (e) { if (/failed to parse protoset/.test(e.message || '')) { /* recompile the descriptor set; or drop ProtosetFile and use reflection */ } } Prevention
- Generate descriptor sets only with protoc --descriptor_set_out (binary format)
- Never point ProtosetFile at .proto source, JSON descriptors, or base64 blobs
- Smoke-test each shipped protoset against a reflection-capable server before release
When it happens
Trigger: opts.ProtosetFile points at a .proto source file ('syntax = "proto3";' text); a .json descriptor or grpcurl text dump; a truncated or base64/gzip-wrapped descriptor that was never decoded back to binary; an empty file.
Common situations: Authors assuming the library compiles proto source on the fly; passing descriptor files produced by tooling that emits a different format; hand-editing or re-saving the protoset as text.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- grpc target host cannot be empty
- failed to build descriptor source from protoset: %w
- failed to build request parser: %w
- validation failed for these fields
- no input provider found
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/6b98a77a62657c1f.
Report an issue: GitHub.