diff --git a/Cargo.toml b/Cargo.toml
index 3bd92d0eb..219d65bfe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -57,7 +57,8 @@ hyper-openssl = "0.10.2"
 hyper-rustls = { version = "0.27.0", default-features = false }
 hyper-socks2 = { version = "0.9.0", default-features = false }
 hyper-timeout = "0.5.1"
-json-patch = "1.0.0"
+json-patch = "2.0.0"
+jsonptr = "0.4.7"
 jsonpath-rust = "0.5.0"
 k8s-openapi = { version = "0.22.0", default-features = false }
 openssl = "0.10.36"
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index cce3dccf8..e735a1f3d 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -28,6 +28,7 @@ garde = { version = "0.18.0", default-features = false, features = ["derive"] }
 anyhow.workspace = true
 futures = { workspace = true, features = ["async-await"] }
 jsonpath-rust.workspace = true
+jsonptr.workspace = true
 kube = { path = "../kube", version = "^0.91.0", default-features = false, features = ["admission"] }
 kube-derive = { path = "../kube-derive", version = "^0.91.0", default-features = false } # only needed to opt out of schema
 k8s-openapi.workspace = true
diff --git a/examples/admission_controller.rs b/examples/admission_controller.rs
index 50cc87d69..10b9c6573 100644
--- a/examples/admission_controller.rs
+++ b/examples/admission_controller.rs
@@ -1,8 +1,11 @@
+use jsonptr::Pointer;
 use kube::core::{
     admission::{AdmissionRequest, AdmissionResponse, AdmissionReview},
     DynamicObject, Resource, ResourceExt,
 };
-use std::{convert::Infallible, error::Error};
+
+use kube::runtime::finalizer;
+use std::{convert::Infallible, error::Error, str::FromStr};
 use tracing::*;
 use warp::{reply, Filter, Reply};
 
@@ -75,13 +78,13 @@ fn mutate(res: AdmissionResponse, obj: &DynamicObject) -> Result<AdmissionRespon
         // Ensure labels exist before adding a key to it
         if obj.meta().labels.is_none() {
             patches.push(json_patch::PatchOperation::Add(json_patch::AddOperation {
-                path: "/metadata/labels".into(),
+                path: Pointer::new(["metadata", "labels"]),
                 value: serde_json::json!({}),
             }));
         }
         // Add our label
         patches.push(json_patch::PatchOperation::Add(json_patch::AddOperation {
-            path: "/metadata/labels/admission".into(),
+            path: Pointer::new(["metadata", "labels", "admission"]),
             value: serde_json::Value::String("modified-by-admission-controller".into()),
         }));
         Ok(res.with_patch(json_patch::Patch(patches))?)
diff --git a/kube-core/Cargo.toml b/kube-core/Cargo.toml
index 9b38f0335..66f528e25 100644
--- a/kube-core/Cargo.toml
+++ b/kube-core/Cargo.toml
@@ -41,3 +41,4 @@ k8s-openapi = { workspace = true, features = ["latest"] }
 assert-json-diff.workspace = true
 kube = { path = "../kube", version = "<1.0.0, >=0.53.0" }
 serde_yaml.workspace = true
+jsonptr.workspace = true
diff --git a/kube-core/src/admission.rs b/kube-core/src/admission.rs
index f800e20ff..afcff1587 100644
--- a/kube-core/src/admission.rs
+++ b/kube-core/src/admission.rs
@@ -223,11 +223,12 @@ pub enum Operation {
 ///     .into_review();
 ///
 /// use json_patch::{AddOperation, Patch, PatchOperation};
+/// use jsonptr::Pointer;
 ///
 /// // A response adding a label to the resource.
 /// let _: AdmissionReview<_> = AdmissionResponse::from(&req)
 ///     .with_patch(Patch(vec![PatchOperation::Add(AddOperation {
-///         path: "/metadata/labels/my-label".to_owned(),
+///         path: Pointer::new(["metadata","labels","my-label"]),
 ///         value: serde_json::Value::String("my-value".to_owned()),
 ///     })]))
 ///     .unwrap()
diff --git a/kube-runtime/Cargo.toml b/kube-runtime/Cargo.toml
index 176013d8f..4d53c217a 100644
--- a/kube-runtime/Cargo.toml
+++ b/kube-runtime/Cargo.toml
@@ -41,6 +41,7 @@ tokio = { workspace = true, features = ["time"] }
 tokio-util = { workspace = true, features = ["time"] }
 tracing.workspace = true
 json-patch.workspace = true
+jsonptr.workspace = true
 serde_json.workspace = true
 thiserror.workspace = true
 backoff.workspace = true
diff --git a/kube-runtime/src/finalizer.rs b/kube-runtime/src/finalizer.rs
index 4800e3a84..32cbaa70d 100644
--- a/kube-runtime/src/finalizer.rs
+++ b/kube-runtime/src/finalizer.rs
@@ -2,12 +2,14 @@
 use crate::controller::Action;
 use futures::{TryFuture, TryFutureExt};
 use json_patch::{AddOperation, PatchOperation, RemoveOperation, TestOperation};
+use jsonptr::Pointer;
 use kube_client::{
     api::{Patch, PatchParams},
     Api, Resource, ResourceExt,
 };
+
 use serde::{de::DeserializeOwned, Serialize};
-use std::{error::Error as StdError, fmt::Debug, sync::Arc};
+use std::{error::Error as StdError, fmt::Debug, str::FromStr, sync::Arc};
 use thiserror::Error;
 
 #[derive(Debug, Error)]
@@ -25,6 +27,8 @@ where
     RemoveFinalizer(#[source] kube_client::Error),
     #[error("object has no name")]
     UnnamedObject,
+    #[error("invalid finalizer")]
+    InvalidFinalizer,
 }
 
 struct FinalizerState {
@@ -138,10 +142,14 @@ where
                     // `Test` ensures that we fail instead of deleting someone else's finalizer
                     // (in which case a new `Cleanup` event will be sent)
                     PatchOperation::Test(TestOperation {
-                        path: finalizer_path.clone(),
+                        path: Pointer::from_str(finalizer_path.as_str())
+                            .map_err(|_err| Error::InvalidFinalizer)?,
                         value: finalizer_name.into(),
                     }),
-                    PatchOperation::Remove(RemoveOperation { path: finalizer_path }),
+                    PatchOperation::Remove(RemoveOperation {
+                        path: Pointer::from_str(finalizer_path.as_str())
+                            .map_err(|_err| Error::InvalidFinalizer)?,
+                    }),
                 ])),
             )
             .await
@@ -156,11 +164,13 @@ where
             let patch = json_patch::Patch(if obj.finalizers().is_empty() {
                 vec![
                     PatchOperation::Test(TestOperation {
-                        path: "/metadata/finalizers".to_string(),
+                        path: Pointer::from_str("/metadata/finalizers")
+                            .map_err(|_err| Error::InvalidFinalizer)?,
                         value: serde_json::Value::Null,
                     }),
                     PatchOperation::Add(AddOperation {
-                        path: "/metadata/finalizers".to_string(),
+                        path: Pointer::from_str("/metadata/finalizers")
+                            .map_err(|_err| Error::InvalidFinalizer)?,
                         value: vec![finalizer_name].into(),
                     }),
                 ]
@@ -170,11 +180,13 @@ where
                     // https://github.com/kube-rs/kube/issues/964#issuecomment-1197311254),
                     // so we need to fail and retry if anyone else has added the finalizer in the meantime
                     PatchOperation::Test(TestOperation {
-                        path: "/metadata/finalizers".to_string(),
+                        path: Pointer::from_str("/metadata/finalizers")
+                            .map_err(|_err| Error::InvalidFinalizer)?,
                         value: obj.finalizers().into(),
                     }),
                     PatchOperation::Add(AddOperation {
-                        path: "/metadata/finalizers/-".to_string(),
+                        path: Pointer::from_str("/metadata/finalizers/-")
+                            .map_err(|_err| Error::InvalidFinalizer)?,
                         value: finalizer_name.into(),
                     }),
                 ]