This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new c34d2c36f2a [v3-1-test] Add 404 handling for non-existent Dag (#61131) 
(#61225)
c34d2c36f2a is described below

commit c34d2c36f2a315bb6546a6de8176d2b24bebe18b
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jan 29 15:52:56 2026 -0500

    [v3-1-test] Add 404 handling for non-existent Dag (#61131) (#61225)
    
    (cherry picked from commit 4b34c4206e12b419d28a6d365e9973dc94979d0c)
    
    Co-authored-by: Yeonguk Choo <[email protected]>
---
 .../src/airflow/ui/public/i18n/locales/en/dag.json |  6 ++
 airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx  |  7 +++
 .../src/airflow/ui/src/pages/Dag/DagNotFound.tsx   | 70 ++++++++++++++++++++++
 3 files changed, 83 insertions(+)

diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json 
b/airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json
index 8085fcb942d..8241c26ccae 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json
@@ -73,6 +73,12 @@
     "navigation": "Navigation: Shift+{{arrow}}",
     "toggleGroup": "Toggle group: Space"
   },
+  "notFound": {
+    "back": "Go Back",
+    "backToDags": "Back to Dags",
+    "message": "The Dag \"{{dagId}}\" does not exist.",
+    "title": "Dag Not Found"
+  },
   "overview": {
     "buttons": {
       "failedRun_one": "Failed Run",
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
index 3ba287faffd..4ea3fa73d55 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
@@ -26,6 +26,7 @@ import { RiArrowGoBackFill } from "react-icons/ri";
 import { useParams } from "react-router-dom";
 
 import { useDagServiceGetDagDetails, useDagServiceGetLatestRunInfo } from 
"openapi/queries";
+import { ApiError } from "openapi/requests/core/ApiError";
 import { TaskIcon } from "src/assets/TaskIcon";
 import { usePluginTabs } from "src/hooks/usePluginTabs";
 import { useRequiredActionTabs } from "src/hooks/useRequiredActionTabs";
@@ -33,6 +34,7 @@ import { DetailsLayout } from 
"src/layouts/Details/DetailsLayout";
 import { useRefreshOnNewDagRuns } from "src/queries/useRefreshOnNewDagRuns";
 import { isStatePending, useAutoRefresh } from "src/utils";
 
+import { DagNotFound } from "./DagNotFound";
 import { Header } from "./Header";
 
 export const Dag = () => {
@@ -103,6 +105,11 @@ export const Dag = () => {
     return true;
   });
 
+  // Handle 404 error when Dag is not found
+  if (error instanceof ApiError && error.status === 404) {
+    return <DagNotFound dagId={dagId} />;
+  }
+
   return (
     <ReactFlowProvider>
       <DetailsLayout error={error ?? runsError} isLoading={isLoading || 
isLoadingRuns} tabs={displayTabs}>
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/DagNotFound.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/DagNotFound.tsx
new file mode 100644
index 00000000000..0a667e8ef63
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/DagNotFound.tsx
@@ -0,0 +1,70 @@
+/*!
+ * 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 { Box, Button, Container, Heading, HStack, Text, VStack } from 
"@chakra-ui/react";
+import { useTranslation } from "react-i18next";
+import { useNavigate } from "react-router-dom";
+
+import { AirflowPin } from "src/assets/AirflowPin";
+
+type DagNotFoundProps = {
+  readonly dagId: string;
+};
+
+export const DagNotFound = ({ dagId }: DagNotFoundProps) => {
+  const navigate = useNavigate();
+  const { t: translate } = useTranslation("dag");
+
+  return (
+    <Box alignItems="center" display="flex" justifyContent="center" pt={36} 
px={4}>
+      <Container maxW="lg">
+        <VStack gap={8} textAlign="center">
+          <AirflowPin height="50px" width="50px" />
+
+          <VStack gap={4}>
+            <Heading>404</Heading>
+            <Text fontSize="lg">{translate("notFound.title")}</Text>
+            <Text color="fg.muted">{translate("notFound.message", { dagId 
})}</Text>
+          </VStack>
+
+          <HStack gap={4}>
+            <Button
+              colorPalette="brand"
+              onClick={() => {
+                void Promise.resolve(navigate(-1));
+              }}
+              size="lg"
+            >
+              {translate("notFound.back")}
+            </Button>
+            <Button
+              colorPalette="brand"
+              onClick={() => {
+                void Promise.resolve(navigate("/dags"));
+              }}
+              size="lg"
+              variant="outline"
+            >
+              {translate("notFound.backToDags")}
+            </Button>
+          </HStack>
+        </VStack>
+      </Container>
+    </Box>
+  );
+};

Reply via email to