apache/beam · error

could not apply mutation for row key=

Error message

could not apply mutation for row key='%s': %v

What it means

In the streaming (DoFn) write path of bigtableio, ProcessElement calls table.Apply for each Mutation. If the Bigtable data client rejects the single-row mutation, the underlying Cloud Bigtable error is wrapped with the affected row key so the failing row can be identified. This indicates the write itself failed, not that the Mutation was structurally invalid.

Solutions

  1. Inspect the wrapped %v error for the root cause (grpc status code) and fix that condition first
  2. Verify the service account has roles/bigtable.user on the instance and that Project/InstanceID/TableName are correct
  3. Validate the row key (non-empty, valid UTF-8/bytes, within size limits) before writing
  4. For large or flaky writes, prefer the batch path (writeBatchFn / ApplyBulk) and retry transient statuses with backoff

Example fix

// before
err = f.table.Apply(ctx, mutation.RowKey, getBigtableMutation(mutation))
if err != nil {
	return fmt.Errorf("could not apply mutation for row key='%s': %v", mutation.RowKey, err)
}
// after: check row key and transient errors before failing
if mutation.RowKey == "" {
	return fmt.Errorf("bigtableio.Mutation has empty row key")
}
if err := f.table.Apply(ctx, mutation.RowKey, getBigtableMutation(mutation)); err != nil {
	if ctx.Err() != nil {
		return ctx.Err() // let the runner retry
	}
	return fmt.Errorf("could not apply mutation for row key='%s': %v", mutation.RowKey, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if mutation.RowKey == "" || len(mutation.Ops) == 0 {
	return fmt.Errorf("invalid mutation: empty row key or no ops")
}

Try / catch

if err := f.table.Apply(ctx, mutation.RowKey, m); err != nil {
	if isTransientGRPC(err) { /* retry with backoff */ }
	return fmt.Errorf("row %q: %w", mutation.RowKey, err)
}

Prevention

When it happens

Trigger: f.table.Apply(ctx, mutation.RowKey, ...) returns a non-nil error in bigtableio.writeFn.ProcessElement (bigtable.go:174) — e.g. server rejects the mutation, context cancelled/deadline exceeded, permission denied on the table, or row key invalid.

Common situations: IAM service account lacking Bigtable user role; row key containing invalid characters or exceeding size limits; mutation exceeding cell/column limits; transient gRPC errors or job cancellation mid-write; wrong project/instance so the table doesn't exist.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/770bbed4859183ec. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/io/bigtableio/bigtable.go:174

	if err := f.client.Close(); err != nil {
		return fmt.Errorf("could not close data operations client: %v", err)
	}
	return nil
}

func (f *writeFn) ProcessElement(ctx context.Context, key int, values func(*Mutation) bool) error {

	var mutation Mutation
	for values(&mutation) {

		err := validateMutation(mutation)
		if err != nil {
			return fmt.Errorf("invalid bigtableio.Mutation: %s", err)
		}

		err = f.table.Apply(ctx, mutation.RowKey, getBigtableMutation(mutation))
		if err != nil {
			return fmt.Errorf("could not apply mutation for row key='%s': %v", mutation.RowKey, err)
		}

	}

	return nil
}

type writeBatchFn struct {
	// Project is the project
	Project string `json:"project"`
	// InstanceID is the bigtable instanceID
	InstanceID string `json:"instanceId"`
	// Client is the bigtable.Client
	client *bigtable.Client `json:"-"`
	// TableName is the qualified table identifier.
	TableName string `json:"tableName"`
	// Table is a bigtable.Table instance with an eventual open connection
	table *bigtable.Table `json:"-"`

View on GitHub (pinned to 12126d8942)