{"record":{"id":"bab9062a1d8cffc2","repo":"projectdiscovery/nuclei","slug":"failed-to-read-protoset-file-w","errorCode":null,"errorMessage":"failed to read protoset file: %w","messagePattern":"failed to read protoset file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/js/libs/grpc/invoke.go","lineNumber":106,"sourceCode":"\treturn grpc.NewClient(\"passthrough:///\"+target, opts...)\n}\n\n// descriptorSource resolves the gRPC method/message schema either from a local\n// compiled protoset file (read through the local-file-access allowlist) or, when\n// no protoset is provided, from server reflection over the existing connection.\n// The returned cleanup func must be called once the source is no longer needed.\nfunc descriptorSource(ctx context.Context, executionID string, cc *grpc.ClientConn, protosetFile string) (grpcurl.DescriptorSource, func(), error) {\n\tnoop := func() {}\n\tif strings.TrimSpace(protosetFile) != \"\" {\n\t\t// resolve through the local-file-access allowlist: unless -lfa is set,\n\t\t// only files inside the nuclei-templates directory are permitted.\n\t\tnormalized, err := protocolstate.NormalizePathWithExecutionId(executionID, protosetFile)\n\t\tif err != nil {\n\t\t\treturn nil, noop, fmt.Errorf(\"protoset path denied: %w\", err)\n\t\t}\n\t\tdata, err := os.ReadFile(normalized)\n\t\tif err != nil {\n\t\t\treturn nil, noop, fmt.Errorf(\"failed to read protoset file: %w\", err)\n\t\t}\n\t\tfds := &descriptorpb.FileDescriptorSet{}\n\t\tif err := proto.Unmarshal(data, fds); err != nil {\n\t\t\treturn nil, noop, fmt.Errorf(\"failed to parse protoset file: %w\", err)\n\t\t}\n\t\tsrc, err := grpcurl.DescriptorSourceFromFileDescriptorSet(fds)\n\t\tif err != nil {\n\t\t\treturn nil, noop, fmt.Errorf(\"failed to build descriptor source from protoset: %w\", err)\n\t\t}\n\t\treturn src, noop, nil\n\t}\n\n\trefClient := grpcreflect.NewClientAuto(ctx, cc)\n\tcleanup := func() { refClient.Reset() }\n\treturn grpcurl.DescriptorSourceFromServer(ctx, refClient), cleanup, nil\n}\n\n// invokeUnary invokes a unary (or single-response) gRPC method described by src","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/js/libs/grpc/invoke.go#L88-L124","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"# before: referenced asset never shipped\no.ProtosetFile = 'acme.protoset'; # -> failed to read protoset file\n\n# after: generate and place under the templates root first\nprotoc --descriptor_set_out=nuclei-templates/grpc/acme.protoset --include_imports acme.proto\n# then in the template\no.ProtosetFile = 'grpc/acme.protoset';","handlingStrategy":"validation","validationCode":"# host-side pre-flight before running the template\ntest -f \"${NUCLEI_TEMPLATES_DIR}/grpc/acme.protoset\" || \\\n  protoc --descriptor_set_out=\"${NUCLEI_TEMPLATES_DIR}/grpc/acme.protoset\" --include_imports acme.proto","typeGuard":null,"tryCatchPattern":"try { const c = new grpc.Client(t, o); c.Connect(); }\ncatch (e) { if (/failed to read protoset/.test(e.message || '')) { /* check path and permissions, regenerate the file */ } }","preventionTips":["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)"],"tags":["grpc","filesystem","configuration"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}