kubernetes/kops · error
error updating object: %w
Error message
error updating object: %w
What it means
Update wraps errors from the dynamic resource client's Update call. Like the patch wrapper, it preserves the underlying API error; failures usually mean the object changed on the server (conflict), is invalid, or the request was rejected.
Source
Thrown at pkg/applylib/applyset/unstructuredclient.go:104
name := nn.Name
patched, err := dynamicResource.Patch(ctx, name, patchType, data, opt)
if err != nil {
return nil, fmt.Errorf("error patching object: %w", err)
}
return patched, nil
}
// Update performs an Update operation on the object. Generally we should prefer server-side-apply.
func (c *UnstructuredClient) Update(ctx context.Context, obj *unstructured.Unstructured, opt metav1.UpdateOptions) (*unstructured.Unstructured, error) {
gvk := obj.GroupVersionKind()
dynamicResource, err := c.dynamicResource(ctx, gvk, obj.GetNamespace())
if err != nil {
return nil, err
}
updated, err := dynamicResource.Update(ctx, obj, opt)
if err != nil {
return nil, fmt.Errorf("error updating object: %w", err)
}
return updated, nil
}
// Get reads the specified object.
func (c *UnstructuredClient) Get(ctx context.Context, gvk schema.GroupVersionKind, nn types.NamespacedName) (*unstructured.Unstructured, error) {
dynamicResource, err := c.dynamicResource(ctx, gvk, nn.Namespace)
if err != nil {
return nil, err
}
obj, err := dynamicResource.Get(ctx, nn.Name, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("could not get existing object: %w", err)
}
return obj, nil
}View on GitHub (pinned to 4c8573c808)
Solutions
- Get a fresh copy of the object, re-apply your changes onto it, and retry Update (handles resourceVersion conflicts)
- Inspect the wrapped error with apierrors.IsConflict/IsInvalid/IsForbidden to branch appropriately
- For declarative workflows prefer server-side apply (Patch with ApplyPatchType) over Update to reduce conflicts
- Fix invalid object fields flagged by the API server before retrying
Example fix
// before
updated, err := client.Update(ctx, obj, metav1.UpdateOptions{})
// after
updated, err := client.Update(ctx, obj, metav1.UpdateOptions{})
if err != nil {
if apierrors.IsConflict(err) {
// re-fetch obj, reapply change, retry with backoff
}
return updated, err
} Defensive patterns
Strategy: retry
Validate before calling
if obj.GetNamespace() == "" && requiresNamespace(gvk) {
return fmt.Errorf("pre-check: object %s needs a namespace before update", gvk.Kind)
} Type guard
func isUpdateConflict(err error) bool {
return apierrors.IsConflict(errors.Unwrap(err))
} Try / catch
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
fresh, err := client.Get(ctx, gvk, nn)
if err != nil {
return err
}
applyChanges(fresh)
_, err = client.Update(ctx, fresh, metav1.UpdateOptions{})
return err
}) Prevention
- Always retry on conflict by re-fetching and re-applying changes
- Prefer server-side apply to avoid resourceVersion churn
- Avoid multiple concurrent writers updating the same object
- Validate object fields before Update to avoid 422 rejections
When it happens
Trigger: Calling UnstructuredClient.Update on an unstructured object where the API server rejects the write: 409 Conflict on stale resourceVersion, 422 validation failure, 403 RBAC denial, or object not found.
Common situations: Optimistic-concurrency conflicts when another controller (or the same apply loop) modified the object; editing objects with defaulted/immutable fields; concurrent apply-set workers updating the same resource.
Related errors
- error replacing instanceGroup: %v
- error patching status: %w
- error updating SSHCredential: %v
- error adding needs-update label: %v
- error applying annotation to record addon installation: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/2c21f820c04108bb.
Report an issue: GitHub.