diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstanceButton.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstanceButton.tsx
new file mode 100644
index 0000000000000..f941db3de2fcd
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstanceButton.tsx
@@ -0,0 +1,77 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useDisclosure } from "@chakra-ui/react";
+import { FiTrash2 } from "react-icons/fi";
+
+import type { TaskInstanceResponse } from "openapi/requests/types.gen";
+import DeleteDialog from "src/components/DeleteDialog";
+import ActionButton from "src/components/ui/ActionButton";
+import { useDeleteTaskInstance } from "src/queries/useDeleteTaskInstance";
+
+type DeleteTaskInstanceButtonProps = {
+ readonly taskInstance: TaskInstanceResponse;
+ readonly withText?: boolean;
+};
+
+const DeleteTaskInstanceButton = ({ taskInstance, withText = true }: DeleteTaskInstanceButtonProps) => {
+ const { onClose, onOpen, open } = useDisclosure();
+
+ const { isPending, mutate: deleteTaskInstance } = useDeleteTaskInstance({
+ dagId: taskInstance.dag_id,
+ dagRunId: taskInstance.dag_run_id,
+ mapIndex: taskInstance.map_index,
+ onSuccessConfirm: () => {
+ onClose();
+ },
+ taskId: taskInstance.task_id,
+ });
+
+ return (
+ <>
+ }
+ onClick={onOpen}
+ text="Delete Task Instance"
+ variant="solid"
+ withText={withText}
+ />
+
+ {
+ deleteTaskInstance({
+ dagId: taskInstance.dag_id,
+ dagRunId: taskInstance.dag_run_id,
+ mapIndex: taskInstance.map_index,
+ taskId: taskInstance.task_id,
+ });
+ }}
+ open={open}
+ resourceName={taskInstance.task_id}
+ title="Delete Task Instance"
+ warningText="This will remove all metadata related to the Task Instance."
+ />
+ >
+ );
+};
+
+export default DeleteTaskInstanceButton;
diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
index 54e3139c5736a..74937073e493b 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
@@ -36,6 +36,7 @@ import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searc
import { getDuration, useAutoRefresh, isStatePending } from "src/utils";
import { getTaskInstanceLink } from "src/utils/links";
+import DeleteTaskInstanceButton from "./DeleteTaskInstanceButton";
import { TaskInstancesFilter } from "./TaskInstancesFilter";
type TaskInstanceRow = { row: { original: TaskInstanceResponse } };
@@ -155,6 +156,7 @@ const taskInstanceColumns = (
+
),
enableSorting: false,
diff --git a/airflow-core/src/airflow/ui/src/queries/useDeleteTaskInstance.ts b/airflow-core/src/airflow/ui/src/queries/useDeleteTaskInstance.ts
new file mode 100644
index 0000000000000..e299b208e1a8f
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/queries/useDeleteTaskInstance.ts
@@ -0,0 +1,80 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+
+import {
+ useTaskInstanceServiceDeleteTaskInstance,
+ useTaskInstanceServiceGetTaskInstanceKey,
+ useTaskInstanceServiceGetTaskInstancesKey,
+ UseGridServiceGridDataKeyFn,
+ useDagRunServiceGetDagRunsKey,
+ UseDagRunServiceGetDagRunKeyFn,
+} from "openapi/queries";
+import { toaster } from "src/components/ui";
+
+type DeleteTaskInstanceParams = {
+ dagId: string;
+ dagRunId: string;
+ mapIndex?: number;
+ onSuccessConfirm: () => void;
+ taskId: string;
+};
+
+const onError = () => {
+ toaster.create({
+ description: "Delete Task Instance request failed.",
+ title: "Failed to delete Task Instance",
+ type: "error",
+ });
+};
+
+export const useDeleteTaskInstance = ({
+ dagId,
+ dagRunId,
+ mapIndex,
+ onSuccessConfirm,
+ taskId,
+}: DeleteTaskInstanceParams) => {
+ const queryClient = useQueryClient();
+
+ const onSuccess = async () => {
+ const queryKeys = [
+ UseDagRunServiceGetDagRunKeyFn({ dagId, dagRunId }),
+ [useDagRunServiceGetDagRunsKey],
+ [useTaskInstanceServiceGetTaskInstancesKey],
+ [useTaskInstanceServiceGetTaskInstanceKey, { dagId, dagRunId, mapIndex, taskId }],
+ UseGridServiceGridDataKeyFn({ dagId }, [{ dagId }]),
+ ];
+
+ await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ queryKey: key })));
+
+ toaster.create({
+ description: "The Task Instance deletion request was successful.",
+ title: "Task Instance Deleted Successfully",
+ type: "success",
+ });
+
+ onSuccessConfirm();
+ };
+
+ return useTaskInstanceServiceDeleteTaskInstance({
+ onError,
+ onSuccess,
+ });
+};