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

  1. Check whether the operation was cancelled intentionally (interrupt or timeout) before treating as a failure.
  2. Inspect the wrapped error's status code via status.Code(err) to distinguish Canceled/Unavailable from real failures.
  3. Increase operation timeouts for long-running actions.
  4. 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.
  5. 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

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


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)