StackAssertion cause is now in extraData

This commit is contained in:
Konstantin Wohlwend 2024-12-11 17:36:43 -08:00
parent 2bbd9b017f
commit b0db0a5f78
7 changed files with 27 additions and 32 deletions

View File

@ -99,7 +99,7 @@ export async function sendEmail({
html
}));
} catch (error) {
const extraData = { host: emailConfig.host, from: emailConfig.senderEmail, to, subject };
const extraData = { host: emailConfig.host, from: emailConfig.senderEmail, to, subject, cause: error };
const temporaryErrorIndicators = [
"450 ",
"Client network socket disconnected before secure TLS connection was established",
@ -113,7 +113,7 @@ export async function sendEmail({
}
// TODO if using custom email config, we should notify the developer instead of throwing an error
throw new StackAssertionError('Failed to send email', extraData, { cause: error });
throw new StackAssertionError('Failed to send email', extraData);
}
}, 3, { exponentialDelayBase: 2000 }));
} finally {

View File

@ -125,7 +125,7 @@ export async function logEvent<T extends EventType[]>(
data = await eventType.dataSchema.validate(data, { strict: true, stripUnknown: false });
} catch (error) {
if (error instanceof yup.ValidationError) {
throw new StackAssertionError(`Invalid event data for event type: ${eventType.id}`, { eventType, data, error, originalData, originalEventTypes: eventTypes }, { cause: error });
throw new StackAssertionError(`Invalid event data for event type: ${eventType.id}`, { eventType, data, error, originalData, originalEventTypes: eventTypes, cause: error });
}
throw error;
}

View File

@ -158,7 +158,7 @@ export abstract class OAuthBaseProvider {
if (error?.error === 'access_denied') {
throw new KnownErrors.OAuthProviderAccessDenied();
}
throw new StackAssertionError(`Inner OAuth callback failed due to error: ${error}`, undefined, { cause: error });
throw new StackAssertionError(`Inner OAuth callback failed due to error: ${error}`, { cause: error });
}
tokenSet = processTokenSet(this.constructor.name, tokenSet, this.defaultAccessTokenExpiresInMillis);

View File

@ -1,16 +1,16 @@
import "../polyfills";
import * as yup from "yup";
import { SmartRouteHandler, routeHandlerTypeHelper, createSmartRouteHandler } from "./smart-route-handler";
import { CrudSchema, CrudTypeOf, CrudlOperation } from "@stackframe/stack-shared/dist/crud";
import { FilterUndefined } from "@stackframe/stack-shared/dist/utils/objects";
import { typedIncludes } from "@stackframe/stack-shared/dist/utils/arrays";
import { deindent, typedToLowercase } from "@stackframe/stack-shared/dist/utils/strings";
import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
import { SmartRequestAuth } from "./smart-request";
import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
import { UsersCrud } from "@stackframe/stack-shared/dist/interface/crud/users";
import { yupArray, yupBoolean, yupMixed, yupNumber, yupObject, yupString, yupValidate } from "@stackframe/stack-shared/dist/schema-fields";
import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
import { typedIncludes } from "@stackframe/stack-shared/dist/utils/arrays";
import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
import { FilterUndefined } from "@stackframe/stack-shared/dist/utils/objects";
import { deindent, typedToLowercase } from "@stackframe/stack-shared/dist/utils/strings";
import * as yup from "yup";
import { SmartRequestAuth } from "./smart-request";
import { SmartRouteHandler, createSmartRouteHandler, routeHandlerTypeHelper } from "./smart-route-handler";
type GetAdminKey<T extends CrudTypeOf<any>, K extends Capitalize<CrudlOperation>> = K extends keyof T["Admin"] ? T["Admin"][K] : void;
@ -292,8 +292,7 @@ async function validate<T>(obj: unknown, schema: yup.ISchema<T>, currentUser: Us
Errors:
${error.errors.join("\n")}
`,
{ obj: JSON.stringify(obj), schema },
{ cause: error }
{ obj: JSON.stringify(obj), schema, cause: error },
);
}
throw error;

View File

@ -61,7 +61,7 @@ async function validate<T>(obj: SmartRequest, schema: yup.Schema<T>, req: NextRe
if (error instanceof yup.ValidationError) {
if (req === null) {
// we weren't called by a HTTP request, so it must be a logical error in a manual invocation
throw new StackAssertionError("Request validation failed", {}, { cause: error });
throw new StackAssertionError("Request validation failed", { cause: error });
} else {
const inners = error.inner.length ? error.inner : [error];
const description = schema.describe();

View File

@ -45,7 +45,7 @@ async function validate<T>(req: NextRequest | null, obj: unknown, schema: yup.Sc
},
});
} catch (error) {
throw new StackAssertionError(`Error occurred during ${req ? `${req.method} ${req.url}` : "a custom endpoint invocation's"} response validation: ${error}`, { obj, schema, error }, { cause: error });
throw new StackAssertionError(`Error occurred during ${req ? `${req.method} ${req.url}` : "a custom endpoint invocation's"} response validation: ${error}`, { obj, schema, cause: error });
}
}

View File

@ -1,5 +1,6 @@
import { globalVar } from "./globals";
import { Json } from "./json";
import { pick } from "./objects";
export function throwErr(errorMessage: string, extraData?: any): never;
@ -62,26 +63,21 @@ export function concatStacktraces(first: Error, ...errors: Error[]): void {
}
export class StackAssertionError extends Error implements ErrorWithCustomCapture {
constructor(message: string, public readonly extraData?: Record<string, any>, options?: ErrorOptions) {
export class StackAssertionError extends Error {
constructor(message: string, public readonly extraData?: Record<string, any> & ErrorOptions) {
const disclaimer = `\n\nThis is likely an error in Stack. Please make sure you are running the newest version and report it.`;
super(`${message}${message.endsWith(disclaimer) ? "" : disclaimer}`, options);
}
super(`${message}${message.endsWith(disclaimer) ? "" : disclaimer}`, pick(extraData ?? {}, ["cause"]));
customCaptureExtraArgs = [
{
...this.extraData,
...this.cause ? { cause: this.cause } : {},
},
];
Object.defineProperty(this, "customCaptureExtraArgs", {
get() {
return [this.extraData];
},
enumerable: false,
});
}
}
StackAssertionError.prototype.name = "StackAssertionError";
export type ErrorWithCustomCapture = {
customCaptureExtraArgs: any[],
};
const errorSinks = new Set<(location: string, error: unknown, ...extraArgs: unknown[]) => void>();
export function registerErrorSink(sink: (location: string, error: unknown) => void): void {
if (errorSinks.has(sink)) {
@ -182,7 +178,7 @@ export class StatusError extends Error {
super(message);
this.statusCode = status;
if (!message) {
throw new StackAssertionError("StatusError always requires a message unless a Status object is passed", {}, { cause: this });
throw new StackAssertionError("StatusError always requires a message unless a Status object is passed", { cause: this });
}
}