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

  1. Compile the schema to a real descriptor set: protoc --descriptor_set_out=acme.protoset --include_imports acme.proto
  2. Sanity-check the artifact: a valid protoset is binary and non-empty, it does not start with 'syntax ='
  3. 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

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

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/6b98a77a62657c1f. Report an issue: GitHub.