projectdiscovery/nuclei · error
failed to read protoset file: %w
Error message
failed to read protoset file: %w
What it means
The protoset path passed the local-file-access allowlist check, but os.ReadFile failed: the file does not exist at the normalized path, or the nuclei process lacks read permission on it. The underlying OS error is wrapped verbatim.
Source
Thrown at pkg/js/libs/grpc/invoke.go:106
return grpc.NewClient("passthrough:///"+target, opts...)
}
// descriptorSource resolves the gRPC method/message schema either from a local
// 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 srcView on GitHub (pinned to 265b3a3dec)
Solutions
- Verify the file exists under the templates root and fix the path/filename
- Regenerate the descriptor set: protoc --descriptor_set_out=acme.protoset --include_imports acme.proto
- Check read permissions for the user running nuclei (frequent in containers)
Example fix
# before: referenced asset never shipped o.ProtosetFile = 'acme.protoset'; # -> failed to read protoset file # after: generate and place under the templates root first protoc --descriptor_set_out=nuclei-templates/grpc/acme.protoset --include_imports acme.proto # then in the template o.ProtosetFile = 'grpc/acme.protoset';
Defensive patterns
Strategy: validation
Validate before calling
# host-side pre-flight before running the template
test -f "${NUCLEI_TEMPLATES_DIR}/grpc/acme.protoset" || \
protoc --descriptor_set_out="${NUCLEI_TEMPLATES_DIR}/grpc/acme.protoset" --include_imports acme.proto Try / catch
try { const c = new grpc.Client(t, o); c.Connect(); }
catch (e) { if (/failed to read protoset/.test(e.message || '')) { /* check path and permissions, regenerate the file */ } } Prevention
- Include the protoset asset in the template distribution and verify it in CI
- Use the exact filename referenced by the template (.protoset, not .proto)
- Check that the nuclei process user can read the file (containers, volume mounts)
When it happens
Trigger: opts.ProtosetFile = 'grpc/acme.protoset' when the file was never added to the templates tree; wrong filename or extension (.proto vs .protoset); file present but unreadable due to permissions or a broken container volume mount.
Common situations: Template distributed without its protoset asset; partially cloned templates directory; protoc output written to a different folder than the one referenced; CI running as an unprivileged user.
Related errors
- could not read profile file: %w
- could not create temp directory: %w
- protoset path denied: %w
- headless mode (-headless) is required if -ho, -sb, -sc or -l
- both follow host redirects and follow redirects specified
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/bab9062a1d8cffc2.
Report an issue: GitHub.