hashicorp/terraform · error
failed to send completed event
Error message
failed to send completed event: %w
What it means
Returned by the InvokeAction streaming handler when server.Send of the Completed event fails. The action itself already ran; only the final gRPC stream write back to Terraform core failed. The %w carries the underlying transport error (typically a status code such as Canceled or Unavailable).
Solutions
- Check whether the operation was cancelled intentionally (interrupt or timeout) before treating as a failure.
- Inspect the wrapped error's status code via status.Code(err) to distinguish Canceled/Unavailable from real failures.
- Increase operation timeouts for long-running actions.
- Re-run the action, but first verify remote side effects: a Completed-delivery failure does not guarantee the action's side effects were not applied.
- Verify network stability between terraform core and the provider process.
Example fix
// before
if err := server.Send(completedEvt); err != nil {
return fmt.Errorf("failed to send completed event: %w", err)
}
// after: keep the error but make it actionable to callers
if err := server.Send(completedEvt); err != nil {
return status.Errorf(codes.Unavailable, "failed to send completed event (action may still have applied): %v", err)
} Defensive patterns
Strategy: try-catch
Try / catch
// Completed-event delivery failure is a transport/cancellation issue.
err := runInvoke(client, actionType, config)
if err != nil {
st, ok := status.FromError(err)
if ok && (st.Code() == codes.Canceled || st.Code() == codes.DeadlineExceeded) {
// client cancelled; verify remote side effects, do not blindly retry
} else if ok && st.Code() == codes.Unavailable {
// transport dropped; safe to retry after reconnect
}
} Prevention
- For long-running actions, set a context deadline generous enough to outlast the action plus the final send.
- Before retrying, verify the action's side effects did not already apply.
- Surface status codes so cancellation is distinguishable from real failure.
When it happens
Trigger: server.Send of the InvokeAction_Event_Completed_ message returns a non-nil error because the client disconnected, the context was cancelled, or the transport broke during the final write.
Common situations: User interrupted the action (Ctrl-C); context deadline exceeded on a long-running action; gRPC connection dropped; terraform core exited mid-action.
Related errors
- wrapped err
- action schema not found for action
- error writing state
- expected to receive state in
- failed to send completed event
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/e9328ea1995333bb.
Report an issue: GitHub.
Appendix: source
Thrown at internal/grpcwrap/provider.go:933
server.Send(&tfplugin5.InvokeAction_Event{
Type: &tfplugin5.InvokeAction_Event_Progress_{
Progress: &tfplugin5.InvokeAction_Event_Progress{
Message: invokeEvt.Message,
},
},
})
case providers.InvokeActionEvent_Completed:
completed := &tfplugin5.InvokeAction_Event_Completed{}
completed.Diagnostics = convert.AppendProtoDiag(completed.Diagnostics, invokeEvt.Diagnostics)
err := server.Send(&tfplugin5.InvokeAction_Event{
Type: &tfplugin5.InvokeAction_Event_Completed_{
Completed: completed,
},
})
if err != nil {
return fmt.Errorf("failed to send completed event: %w", err)
}
}
}
return nil
}
func (p *provider) ValidateActionConfig(_ context.Context, req *tfplugin5.ValidateActionConfig_Request) (*tfplugin5.ValidateActionConfig_Response, error) {
resp := &tfplugin5.ValidateActionConfig_Response{}
ty := p.schema.Actions[req.TypeName].ConfigSchema.ImpliedType()
configVal, err := decodeDynamicValue(req.Config, ty)
if err != nil {
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
return resp, nil
}
View on GitHub (pinned to d32a084675)