pulumi/pulumi · error
analyzer has not had handshake called
Error message
analyzer has not had handshake called
What it means
The analyzer gRPC server requires that the Handshake RPC be called before any other RPC so it can learn the engine address and root directory. ConfigureStack is called before the handshake state is stored (srv.handshake == nil), meaning the engine and the analyzer plugin are speaking out of order or a non-Pulumi caller invoked the RPC directly. The library throws this to fail fast rather than constructing a context with a missing EngineAddress.
Source
Thrown at sdk/go/pulumi/policyx/server.go:277
EnforcementLevel: pulumirpc.EnforcementLevel(p.EnforcementLevel()),
ConfigSchema: configSchema,
})
}
return &pulumirpc.AnalyzerInfo{
Name: srv.policyPack.Name(),
Version: srv.policyPack.Version().String(),
Policies: policies,
SupportsConfig: true,
InitialConfig: nil, /* TODO */
}, nil
}
func (srv *analyzerServer) ConfigureStack(ctx context.Context,
req *pulumirpc.AnalyzerStackConfigureRequest) (
*pulumirpc.AnalyzerStackConfigureResponse, error,
) {
if srv.handshake == nil {
return nil, errors.New("analyzer has not had handshake called")
}
root := ""
if srv.handshake.RootDirectory != nil {
root = *srv.handshake.RootDirectory
}
info := pulumi.RunInfo{
Stack: req.Stack,
Project: req.Project,
Organization: req.Organization,
RootDirectory: root,
MonitorAddr: "",
EngineAddr: srv.handshake.EngineAddress,
Config: req.Config,View on GitHub (pinned to 793f7b2e16)
Solutions
- Upgrade the Pulumi CLI/engine so Handshake is always sent before AnalyzerStackConfigure
- If you invoke the analyzer server yourself, call Handshake first and pass its result (EngineAddress, RootDirectory) before ConfigureStack
- In tests, construct the analyzer via analyzerServe/handshake flow instead of calling srv.ConfigureStack directly
Example fix
// before
srv.ConfigureStack(ctx, req) // srv.handshake == nil
// after
hs, err := srv.Handshake(ctx, &pulumirpc.AnalyzerHandshakeRequest{EngineAddress: engineAddr})
if err != nil { return err }
// now ConfigureStack can proceed
srv.ConfigureStack(ctx, req) Defensive patterns
Strategy: validation
Validate before calling
// when driving the analyzer directly
if srvHandshake == nil {
return fmt.Errorf("call Handshake before ConfigureStack")
}
_ = srv.ConfigureStack(ctx, req) Type guard
func handshakeDone(h *pulumirpc.AnalyzerHandshakeRequest) bool { return h != nil } Try / catch
if err := srv.ConfigureStack(ctx, req); err != nil {
if strings.Contains(err.Error(), "has not had handshake called") {
// re-run Handshake then retry
}
} Prevention
- Always run the analyzer through AnalyzerMain/RunAnalyzer so the RPC order is managed for you
- In custom drivers, invoke Handshake first, unconditionally
- Pin the CLI version your policy pack supports
When it happens
Trigger: Calling AnalyzerStackConfigure (ConfigureStack) on analyzerServer before Handshake has populated srv.handshake — e.g. a custom driver invoking RPCs in the wrong order, an outdated engine that does not send Handshake first, or a hand-rolled gRPC client/test that skips Handshake.
Common situations: Running an older `pulumi` CLI against a newer policy pack SDK that requires handshake; writing integration tests that call ConfigureStack directly; proxying or replaying analyzer RPC traffic out of order.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- converting analysis results: %w
- fatal: %v
- failed to unmarshal provider for policy %q: %w
- getting analyzer info: %w
- error signaling cancellation to resource provider '%s': %w
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/92f0841dc8899bcd.
Report an issue: GitHub.