pulumi/pulumi · error
callback not found:
Error message
callback not found:
What it means
The PCL interpreter hosts a local callback gRPC server (pclCallbackServer); callbacks are registered under a UUID token and the engine invokes them by token. If an Invoke request carries a token that has no registered function, the server returns "callback not found: <token>". This means the callback was never registered, or it was invoked after the interpreter/session ended.
Source
Thrown at pkg/pcl/runtime/interpreter.go:162
defer s.functionsLock.Unlock()
s.functions[uuidString] = fn
return &pulumirpc.Callback{
Token: uuidString,
Target: "127.0.0.1:" + strconv.Itoa(s.handle.Port),
// PCL decodes strings containing non-UTF8 bytes losslessly, so the engine may send them to callbacks
// hosted here.
AcceptsByteString: true,
}, nil
}
func (s *pclCallbackServer) Invoke(
ctx context.Context, req *pulumirpc.CallbackInvokeRequest,
) (*pulumirpc.CallbackInvokeResponse, error) {
s.functionsLock.RLock()
fn, ok := s.functions[req.Token]
s.functionsLock.RUnlock()
if !ok {
return nil, errors.New("callback not found: " + req.Token)
}
resp, err := fn(ctx, req.Request)
if err != nil {
return nil, err
}
b, err := proto.Marshal(resp)
if err != nil {
return nil, fmt.Errorf("marshaling callback response: %w", err)
}
return &pulumirpc.CallbackInvokeResponse{Response: b}, nil
}
func (i *Interpreter) getCallbackServer() (*pclCallbackServer, error) {
i.callbacksOnce.Do(func() {
s, err := newPCLCallbackServer()
i.callbacks = s
i.callbacksErr = err
})View on GitHub (pinned to 793f7b2e16)
Solutions
- Re-run the program so hooks re-register fresh callback tokens; discard stale tokens from prior runs
- Verify the callback Token/Target pair matches the interpreter instance currently serving
- Avoid persisting or replaying callback tokens across deployments/runs
- If concurrent, ensure each interpreter has its own callback server port and tokens are not mixed
Defensive patterns
Strategy: retry
Try / catch
_, err := callbackClient.Invoke(ctx, req)
if err != nil && strings.HasPrefix(err.Error(), "callback not found") {
// re-run the program to obtain fresh tokens; do not blindly retry with same token
} Prevention
- Never reuse callback tokens from previous runs
- Keep token and target port paired to the same interpreter instance
- Register callbacks before any operation that can invoke them
- Avoid replaying serialized engine state that embeds old tokens
When it happens
Trigger: pclCallbackServer.Invoke (pkg/pcl/runtime/interpreter.go:162) looks up req.Token in s.functions and misses — e.g. the engine invokes a stale/expired callback token, or invokes a callback belonging to a different interpreter instance.
Common situations: Hook callbacks invoked after the program finished, retry of a failed operation reusing an old callback token, running multiple concurrent interpreters where tokens are sent to the wrong port, or cached state from a previous run being replayed.
Related errors
- callback panicked: %v %s
- load %q: %w
- failed to get runtime options prompts: %w
- configuring policy pack %q at %q: %w
- invoke engine: %w
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/e2126f151a4edda0.
Report an issue: GitHub.