Change:
/kind bug /sig api-machinery /cc What happened: patching (SMP) any object's annotation using invalid patch content will result in an (500) internal-server error. expecting a (400) bad-request error in this case. patch body: received error: the error is thrown from converter instead of the patcher, so it seems that patcher considers the body as valid then the converter complained. Anything else we need to know?: when my grandpa was 65 years old he started running one mile a day to keep fit. he's 70 now and we have no idea where he is.
I just tried the same patch with "MergePatchType", and it also fails with 500.
I can't make it fail with JSONPatchType, I get a 422
I suspect the error should read "the server rejected your request due to an error in our request"

Discussion:
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", <ins> "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", </ins> "//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/audit:go_default_library",

"k8s.io/apimachinery/pkg/util/mergepatch" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/strategicpatch" <ins> "k8s.io/apimachinery/pkg/util/validation/field" </ins> "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/audit" "k8s.io/apiserver/pkg/authorization/authorizer"

// Construct the resulting typed, unversioned object. objToUpdate := p.restPatcher.New() if err := runtime.DecodeInto(p.codec, patchedObjJS, objToUpdate); err != nil { <del> return nil, err </del> <ins> return nil, errors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{ field.Invalid(field.NewPath("patch"), string(patchedObjJS), err.Error()), }) </ins> } if p.fieldManager != nil {

// Rather than serialize the patched map to JSON, then decode it to an object, we go directly from a map to an object if err := runtime.DefaultUnstructuredConverter.FromUnstructured(patchedObjMap, objToUpdate); err != nil { <del> return err </del> <ins> return errors.NewInvalid(schema.GroupKind{}, "", field.ErrorList{ field.Invalid(field.NewPath("patch"), fmt.Sprintf("%+v", patchMap), err.Error()), }) </ins> } // Decoding from JSON to a versioned object would apply defaults, so we do the same here defaulter.Default(objToUpdate)

} ` <ins> func Test4xxStatusCodeInvalidPatch(t *testing.T) { _, client, closeFn := setup(t) defer closeFn() obj := []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "name": "deployment", "labels": {"app": "nginx"} }, "spec": { "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "labels": { "app": "nginx" } }, "spec": { "containers": [{ "name":  "nginx", "image": "nginx:latest" }] } } } }`) resp, err := client.CoreV1().RESTClient().Post(). AbsPath("/apis/apps/v1"). Namespace("default"). Resource("deployments"). Body(obj).Do().Get() if err != nil { t.Fatalf("Failed to create object: %v: %v", err, resp) } result := client.CoreV1().RESTClient().Patch(types.MergePatchType). AbsPath("/apis/apps/v1"). Namespace("default"). Resource("deployments"). Name("deployment"). Body([]byte(`{"metadata":{"annotations":{"foo":["bar"]}}}`)).Do() var statusCode int result.StatusCode(&statusCode) if statusCode != 422 { t.Fatalf("Expected status code to be 422, got %v (%#v)", statusCode, result) } result = client.CoreV1().RESTClient().Patch(types.StrategicMergePatchType). AbsPath("/apis/apps/v1"). Namespace("default"). Resource("deployments"). Name("deployment"). Body([]byte(`{"metadata":{"annotations":{"foo":["bar"]}}}`)).Do() result.StatusCode(&statusCode) if statusCode != 422 { t.Fatalf("Expected status code to be 422, got %v (%#v)", statusCode, result) } } </ins> // Tests that the apiserver returns 202 status code as expected. func Test202StatusCode(t *testing.T) { s, clientSet, closeFn := setup(t)