import { AlertCircle } from "lucide-react"; import { isRouteErrorResponse } from "react-router"; import { isApiError, isConnectionError } from "~/server/headscale/api/error-client"; import cn from "~/utils/cn"; import Card from "./Card"; import Code from "./Code"; import Link from "./link"; export function getErrorMessage(error: Error | unknown): { title: string; jsxMessage: React.ReactNode; } { if (isRouteErrorResponse(error)) { if (isApiError(error.data)) { const { statusCode, rawData, data, requestUrl } = error.data; if (statusCode >= 500) { return { jsxMessage: ( <> There was an error communicating with the Headscale API.
The server responded with a status code of {statusCode}, indicating a server-side issue. Please check the Headscale server status and try again later.
{(error.data.data != null || error.data.rawData != null) && (
                  {error.data.data != null ? (
                    {JSON.stringify(error.data.data, null, 2)}
                  ) : (
                    {error.data.rawData}
                  )}
                
)} ), title: "Headscale API Error", }; } const authError = error.data.statusCode === 401 || error.data.statusCode === 403; return { jsxMessage: ( <> The Headscale API returned an unexpected response. {authError ? ( <> {" "} The status code indicates an authentication error. Please verify your API key and Headplane configuration. ) : ( <> You may be using an unsupported version of Headscale or this may be a bug. )} Error Details
              {JSON.stringify(error.data, null, 2)}
            
), title: "Invalid response from Headscale API", }; } if (isConnectionError(error.data)) { const { requestUrl, errorCode, errorMessage, extraData } = error.data; return { jsxMessage: ( <> Headplane was unable to reach the Headscale API. Please check your network setup and configuration to ensure Headplane is able to connect. Error Details
              {requestUrl}
              
{errorCode}: {errorMessage} {extraData != null && ( <>

{JSON.stringify(extraData, null, 2)} )}
), title: "Cannot connect to Headscale API", }; } return { jsxMessage: ( <> There was an error processing your request.
Status Code: {error.status}
Status Text: {error.data} ), title: `Error ${error.status}`, }; } if (!(error instanceof Error)) { return { jsxMessage: ( <> An unexpected error occurred which is most likely a bug. Please consider reporting filing an issue on the{" "} Headplane GitHub {" "} repository with the details below. Error Details
            {JSON.stringify(error, null, 2)}
          
), title: "Unexpected Error", }; } // Traverse the error chain to find the root cause let rootError = error; if (error.cause != null) { rootError = error.cause as Error; while (rootError.cause != null) { rootError = rootError.cause as Error; } } // TODO: If we are aggregate, concat into a single message if (rootError instanceof AggregateError) { throw new Error("AggregateError handling not implemented yet"); } return { jsxMessage: rootError.message, title: rootError.name.length > 0 && rootError.name !== "Error" ? `Error: ${rootError.name}` : "Error", }; } interface ErrorBannerProps { error: unknown; className?: string; } export function ErrorBanner({ error, className }: ErrorBannerProps) { const { title, jsxMessage } = getErrorMessage(error); return (
{title}
{jsxMessage}
); }