Repository: apache/airflow
File: airflow-core/src/airflow/ui/src/queries/useTrigger.ts (TypeScript)

Code before:
      [useDagServiceGetDagsUiKey],
      UseDagRunServiceGetDagRunsKeyFn({ dagId }, [{ dagId }]),
      UseTaskInstanceServiceGetTaskInstancesKeyFn({ dagId, dagRunId: "~" }, [{ dagId, dagRunId: "~" }]),
      UseGridServiceGetGridRunsKeyFn({ dagId }, [{ dagId }]),
    ];

    await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ queryKey: key })));

    toaster.create({
      description: translate("triggerDag.toaster.success.description"),
      title: translate("triggerDag.toaster.success.title"),
      type: "success",
    });
    onSuccessConfirm();

    // Only redirect if we're already on the dag page
    if (selectedDagId === dagRun.dag_id) {
      navigate(`/dags/${dagRun.dag_id}/runs/${dagRun.dag_run_id}`);
    }
  };

  const onError = (_error: unknown) => {
    // Check if error is a 403 (Forbidden) status
    if (isHttpError(_error, 403)) {
      toaster.create({
        description: translate(
          "triggerDag.toaster.error.forbidden.description",
          "You don't have permission to trigger this DAG",
        ),
        title: translate("triggerDag.toaster.error.forbidden.title", "Permission Denied"),
        type: "error",
      });
    } else if (isHttpError(_error, 401)) {
      toaster.create({
        description: translate(
          "triggerDag.toaster.error.unauthorized.description",
          "Please log in to trigger DAGs",
        ),
        title: translate("triggerDag.toaster.error.unauthorized.title", "Authentication Required"),
        type: "error",
      });
    } else {
      // Generic error handling for other status codes
      toaster.create({
        description: translate("triggerDag.toaster.error.generic.description", "Failed to trigger DAG"),
        title: translate("triggerDag.toaster.error.generic.title", "Error"),
        type: "error",
      });
    }
    setError(_error);
  };

Review comment (suggestion):
Maybe an easier way to handle all cases instead of doing some manual mapping is to just display the `description: error.message`.

Similarly to how we do it in other places, for instance `usePatchTaskInstance`. This way the backend error is shown to the user. 

Code after:
      UseDagRunServiceGetDagRunsKeyFn({ dagId }, [{ dagId }]),
      UseTaskInstanceServiceGetTaskInstancesKeyFn({ dagId, dagRunId: "~" }, [{ dagId, dagRunId: "~" }]),
      UseGridServiceGetGridRunsKeyFn({ dagId }, [{ dagId }]),
    ];

    await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ queryKey: key })));

    toaster.create({
      description: translate("triggerDag.toaster.success.description"),
      title: translate("triggerDag.toaster.success.title"),
      type: "success",
    });
    onSuccessConfirm();

    // Only redirect if we're already on the dag page
    if (selectedDagId === dagRun.dag_id) {
      navigate(`/dags/${dagRun.dag_id}/runs/${dagRun.dag_run_id}`);
    }
  };

  const onError = (_error: Error) => {
    toaster.create({
      description: _error.message,
      title: translate("triggerDag.toaster.error.title"),
      type: "error",
    });
    setError(_error);
  };

  const { isPending, mutate } = useDagRunServiceTriggerDagRun({
    onError,
    onSuccess,
  });

  const triggerDagRun = (dagRunRequestBody: DagRunTriggerParams) => {
    const parsedConfig = JSON.parse(dagRunRequestBody.conf) as Record<string, unknown>;

    const logicalDate = dagRunRequestBody.logicalDate ? new Date(dagRunRequestBody.logicalDate) : undefined;

    // eslint-disable-next-line unicorn/no-null
    const formattedLogicalDate = logicalDate?.toISOString() ?? null;

    const checkDagRunId = dagRunRequestBody.dagRunId === "" ? undefined : dagRunRequestBody.dagRunId;
    const checkNote = dagRunRequestBody.note === "" ? undefined : dagRunRequestBody.note;

    mutate({
      dagId,
      requestBody: {
        conf: parsedConfig,
        dag_run_id: checkDagRunId,
        logical_date: formattedLogicalDate,