hashicorp/terraform · error

wrapped err: %w

Error message

wrapped err: %w

What it means

Returned by WriteStateBytes (internal/grpcwrap/provider6.go:1075) when the bidirectional stream's srv.Recv() returns an error other than io.EOF while reading chunked state-upload messages. The error is wrapped with %w and stored in grpcErr, then returned after the receive loop breaks. The underlying cause is a transport-level stream failure (connection reset, cancellation, deadline, broken pipe).

Source

Thrown at internal/grpcwrap/provider6.go:1075

		rangeStart += byteCount
	}
}

func (p *provider6) WriteStateBytes(srv tfplugin6.Provider_WriteStateBytesServer) error {
	var typeName string
	var stateId string

	state := bytes.Buffer{}
	var grpcErr error
	var totalReceivedBytes int
	var expectedTotalLength int64
	for {
		chunk, err := srv.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			grpcErr = fmt.Errorf("wrapped err: %w", err)
			break
		}
		if expectedTotalLength == 0 {
			// On the first iteration
			expectedTotalLength = chunk.TotalLength // record expected length
			if chunk.Meta != nil {
				// We expect the Meta to be set on the first message, only
				typeName = chunk.Meta.TypeName
				stateId = chunk.Meta.StateId
			} else {
				panic("expected Meta to be set on first chunk sent to WriteStateBytes")
			}
		}

		n, err := state.Write(chunk.Bytes)
		if err != nil {
			return fmt.Errorf("error writing state: %w", err)
		}

View on GitHub (pinned to c9def3e214)

Solutions

  1. Inspect the wrapped error (errors.Unwrap) to distinguish context cancellation from a transport error.
  2. Retry the operation: state writes are retried by Terraform core; ensure the network/state store is healthy.
  3. Increase gRPC keepalive/timeout allowances if uploads are large and slow.
  4. Check state store backend availability and that the connection is not behind an aggressive proxy.

Example fix

// before
for {
    chunk, err := srv.Recv()
    if err == io.EOF { break }
    if err != nil {
        grpcErr = fmt.Errorf("wrapped err: %w", err)  // connection reset by peer
        break
    }
}

// after (caller side: retry on transient transport error)
if errors.Is(err, context.Canceled) || isTransient(err) {
    // retry the state upload
}
Defensive patterns

Strategy: retry

Validate before calling

// Before uploading, verify the stream/context is alive:
func streamAlive(ctx context.Context) bool {
    return ctx.Err() == nil
}

Type guard

func isTransientRecvErr(err error) bool {
    return errors.Is(err, context.Canceled) ||
        errors.Is(err, context.DeadlineExceeded) ||
        status.Code(err) == codes.Unavailable
}

Try / catch

// Retry the whole WriteStateBytes upload on transient transport errors:
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
    err := uploadState(ctx, bytes)
    if err == nil { return nil }
    lastErr = err
    if !isTransientRecvErr(err) { break }
}
return lastErr

Prevention

When it happens

Trigger: During a state-store upload over the WriteStateBytes server-streaming RPC, the client (Terraform core) drops the connection, the gRPC context is cancelled, or the network link breaks mid-stream; srv.Recv() then returns a non-EOF error and it is wrapped here.

Common situations: Unstable network to a remote state store, gRPC keepalive/timeout killing a long upload, context cancellation (e.g. user Ctrl-C during apply), state store backend restart mid-write, or proxy/load-balancer closing idle streams.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/c1bd29087ba3a114. Report an issue: GitHub.