Fix for SSE error when trying to send when the controller has been aborted

This commit is contained in:
Matt Aitken
2025-03-17 14:29:51 +00:00
parent d5561b5400
commit 3fd4470e89
2 changed files with 37 additions and 23 deletions
@@ -44,25 +44,23 @@ import {
v3ApiKeysPath,
v3BatchesPath,
v3BillingPath,
v3ConcurrencyPath,
v3DeploymentsPath,
v3EnvironmentPath,
v3EnvironmentVariablesPath,
v3ProjectAlertsPath,
v3ProjectPath,
v3ProjectSettingsPath,
v3QueuesPath,
v3RunsPath,
v3SchedulesPath,
v3TestPath,
v3UsagePath,
} from "~/utils/pathBuilder";
import { useDevPresence } from "../DevPresence";
import { ImpersonationBanner } from "../ImpersonationBanner";
import { PackageManagerProvider, TriggerDevStepV3 } from "../SetupCommands";
import { UserProfilePhoto } from "../UserProfilePhoto";
import connectedImage from "../../assets/images/cli-connected.png";
import disconnectedImage from "../../assets/images/cli-disconnected.png";
import { FreePlanUsage } from "../billing/FreePlanUsage";
import { useDevPresence } from "../DevPresence";
import { ImpersonationBanner } from "../ImpersonationBanner";
import { Button, ButtonContent, LinkButton } from "../primitives/Buttons";
import {
Dialog,
@@ -80,18 +78,14 @@ import {
PopoverTrigger,
} from "../primitives/Popover";
import { TextLink } from "../primitives/TextLink";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip";
import { PackageManagerProvider, TriggerDevStepV3 } from "../SetupCommands";
import { UserProfilePhoto } from "../UserProfilePhoto";
import { EnvironmentSelector } from "./EnvironmentSelector";
import { HelpAndFeedback } from "./HelpAndFeedbackPopover";
import { SideMenuHeader } from "./SideMenuHeader";
import { SideMenuItem } from "./SideMenuItem";
import { SideMenuSection } from "./SideMenuSection";
import {
SimpleTooltip,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "../primitives/Tooltip";
type SideMenuUser = Pick<User, "email" | "admin"> & { isImpersonating: boolean };
export type SideMenuProject = Pick<
@@ -194,6 +188,13 @@ export function SideMenu({
to={v3SchedulesPath(organization, project, environment)}
data-action="schedules"
/>
<SideMenuItem
name="Queues"
icon={RectangleStackIcon}
activeIconColor="text-blue-500"
to={v3QueuesPath(organization, project, environment)}
data-action="queues"
/>
<SideMenuItem
name="Deployments"
icon={ServerStackIcon}
@@ -241,14 +242,6 @@ export function SideMenu({
to={v3EnvironmentVariablesPath(organization, project, environment)}
data-action="environment variables"
/>
<SideMenuItem
name="Concurrency limits"
icon={RectangleStackIcon}
activeIconColor="text-indigo-500"
to={v3ConcurrencyPath(organization, project, environment)}
data-action="concurrency"
/>
<SideMenuItem
name="Project settings"
icon={Cog8ToothIcon}
+24 -3
View File
@@ -53,6 +53,26 @@ export function createSSELoader(options: SSEOptions) {
);
};
const createSafeSend = (originalSend: SendFunction): SendFunction => {
return (event) => {
try {
if (!internalController.signal.aborted) {
originalSend(event);
}
// If controller is aborted, silently ignore the send attempt
} catch (error) {
if (error instanceof Error) {
if (error.message?.includes("Controller is already closed")) {
// Silently handle controller closed errors
return;
}
log(`Error sending event: ${error.message}`);
}
throw error; // Re-throw other errors
}
};
};
const context: SSEContext = {
id,
request,
@@ -115,12 +135,13 @@ export function createSSELoader(options: SSEOptions) {
return eventStream(combinedSignal, function setup(send) {
connections.add(id);
const safeSend = createSafeSend(send);
async function run() {
try {
log("Initializing");
if (handlers.initStream) {
const shouldContinue = await handlers.initStream({ send });
const shouldContinue = await handlers.initStream({ send: safeSend });
if (shouldContinue === false) {
log("initStream returned false, so we'll stop the stream");
internalController.abort("Init requested stop");
@@ -138,7 +159,7 @@ export function createSSELoader(options: SSEOptions) {
if (handlers.iterator) {
try {
const shouldContinue = await handlers.iterator({ date, send });
const shouldContinue = await handlers.iterator({ date, send: safeSend });
if (shouldContinue === false) {
log("iterator return false, so we'll stop the stream");
internalController.abort("Iterator requested stop");
@@ -173,7 +194,7 @@ export function createSSELoader(options: SSEOptions) {
log("Cleanup called");
if (handlers.cleanup) {
try {
handlers.cleanup({ send });
handlers.cleanup({ send: safeSend });
} catch (error) {
log(
`Error in cleanup handler: ${