ToolJet/ToolJet · error · GrpcOperationError
Service ${serviceName} is not a valid constructor function
Error message
Service ${serviceName} is not a valid constructor function What it means
Thrown in buildReflectionClient when findServiceInPackage returns a value but typeof service !== 'function'. A gRPC service constructor must be a function; getting a non-function (typically an object or namespace) means the resolved symbol is a nested package/namespace, not a service constructor. The traversal matched a container, not the service itself.
Source
Thrown at plugins/packages/grpcv2/lib/operations.ts:91
if (!descriptor) {
throw new GrpcOperationError(`Service descriptor not found for ${serviceName}`);
}
const packageObject = descriptor.getPackageObject({
keepCase: true,
enums: String,
longs: String,
defaults: true,
oneofs: true
});
const service = findServiceInPackage(packageObject, serviceName);
if (!service) {
throw new GrpcOperationError(`Service ${serviceName} not found in proto descriptor`);
}
if (typeof service !== 'function') {
throw new GrpcOperationError(`Service ${serviceName} is not a valid constructor function`);
}
const credentials = buildChannelCredentials(sourceOptions);
const cleanUrl = sanitizeGrpcServerUrl(sourceOptions.url, sourceOptions.ssl_enabled);
const ServiceConstructor = service as new (url: string, credentials: any) => GrpcClient;
const grpcClient = new ServiceConstructor(cleanUrl, credentials);
return grpcClient;
} catch (error: unknown) {
const err = toError(error);
throw new GrpcOperationError(`Failed to create reflection client for service ${serviceName}: ${err.message}`, error);
}
};
export const buildProtoFileClient = async (sourceOptions: SourceOptions, serviceName: string): Promise<GrpcClient> => {
try {
const packageDefinition = await loadProtoFromRemoteUrl(sourceOptions.proto_file_url!);
const grpcObject = grpc.loadPackageDefinition(packageDefinition);View on GitHub (pinned to 20602a8e10)
Solutions
- Target the fully-qualified service symbol that resolves to the constructor, not an enclosing namespace.
- Verify the proto: a service keyword defines the callable; ensure the name maps to that, not a package or message.
- Retry after refreshing the reflection descriptor cache.
Defensive patterns
Strategy: type-guard
Type guard
const isServiceConstructor = (v: unknown): v is new (url: string, creds: any) => GrpcClient =>
typeof v === 'function';
// usage: if (!isServiceConstructor(sym)) throw new Error(`${name} is a namespace, not a service`); Prevention
- Resolve to the service symbol, not an enclosing namespace/message.
- Disambiguate name collisions with full package paths.
- Validate typeof === 'function' before constructing clients in your own wrappers.
When it happens
Trigger: serviceName resolves to a package or namespace object rather than the service constructor — e.g. the name matches a package that contains the service. A proto where a message or enum name shadows the service name.
Common situations: Service and package sharing a name; pointing at an intermediate namespace; descriptor version where the service symbol is wrapped differently; stale descriptor cached by reflection.
Related errors
- Service ${serviceName} not found in proto descriptor
- Service ${serviceName} has no methods
- Service descriptor not found for ${serviceName}
- Service ${serviceName} not found in proto file
- Service ${serviceName} not found in any proto file
AI-assisted analysis of ToolJet/ToolJet@20602a8e10 (2026-08-13).
Data as JSON: /api/errors/41fb3653c1fe7e4e.
Report an issue: GitHub.