Tighten ActionException wrapping to only accept Error. (#1111)
Docker Hub - Develop / docker-latest (push) Failing after 31s
Tests / Build & Lint (push) Failing after 2m39s
Tests / Unit tests (push) Successful in 2m52s
Tests / Integration tests (push) Failing after 16s
Tests / Application Service Integration tests (push) Failing after 16s
GHCR - Development Branches / ghcr-publish (push) Failing after 12m26s

* Tighten ActionException wrapping to only accept Error.

For months we had a bug where the "exception kind" enum was being
provided as the error instead of the wrapped error in bot sdk wrapper
code. This exception kind enum was optional in the factory method
being used for the MatrixException type and so this resulted in
draupnir reporting simply `undefined` when there were errors.  Which
was a major issue because not only did it make it difficult to track
problems down, but it made the software look like shit. Remarkably we
were still able to remotely diagnose problems essentially blind here.
But it was caused by the ActionException accepting `unknown` for Error
in order to tolerate a number of causes. We figure that this is
unacceptable because it allows for these kinds of bugs and also that
it delays finding out where we are calling broken apis.

Closes https://github.com/the-draupnir-project/Draupnir/issues/759.
Closes https://github.com/the-draupnir-project/planning/issues/137.

* Flakey test idk.
This commit is contained in:
Gnuxie
2026-04-30 19:14:15 +01:00
committed by GitHub
parent ad4ce0706f
commit 60eeb86415
24 changed files with 225 additions and 90 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"draupnir": patch
---
Fixed an issue where sometimes Draupnir would log cryptic errors such as
`undefined: undefined`.
https://github.com/the-draupnir-project/Draupnir/issues/759.
+8
View File
@@ -0,0 +1,8 @@
---
"@the-draupnir-project/matrix-protection-suite": minor
"draupnir": patch
---
Tighten ActionException to only accept `Error` instead of `unknown`, leading to
less mistakes. We now also offer and use a `ensureThrowableIsError` to use when
checking if third-party apis are throwing junk, such as the matrix-bot-sdk.
@@ -29,6 +29,7 @@ import {
RoomCreator,
RoomVersionMirror,
Task,
assertThrowableIsError,
isError,
} from "matrix-protection-suite";
import { Draupnir } from "../Draupnir";
@@ -388,7 +389,7 @@ export class AppServiceDraupnirManager {
return ActionException.Result(
`Could not start draupnir ${clientUserID} for owner ${mjolnirRecord.owner}`,
{
exception: e,
exception: assertThrowableIsError(e),
exceptionKind: ActionExceptionKind.Unknown,
}
);
@@ -13,6 +13,7 @@ import {
RoomStateRevision,
StateChange,
StateEvent,
assertThrowableIsError,
isError,
} from "matrix-protection-suite";
import {
@@ -232,7 +233,7 @@ export class SqliteRoomStateBackingStore
} catch (e) {
return Promise.resolve(
ActionException.Result(`Unable to forget the room ${roomID}`, {
exception: e,
exception: assertThrowableIsError(e),
exceptionKind: ActionExceptionKind.Unknown,
})
);
+5 -1
View File
@@ -15,6 +15,7 @@ import {
Ok,
Revision,
WatchedPolicyRoom,
assertThrowableIsError,
isError,
} from "matrix-protection-suite";
import {
@@ -244,7 +245,10 @@ export const DraupnirRoomsRemoveCommand = describeCommand({
} catch (exception) {
return ActionException.Result(
`Failed to leave ${roomRef.toPermalink()} - the room is no longer being protected, but the bot could not leave.`,
{ exceptionKind: ActionExceptionKind.Unknown, exception }
{
exceptionKind: ActionExceptionKind.Unknown,
exception: assertThrowableIsError(exception),
}
);
}
return Ok(undefined);
@@ -9,6 +9,7 @@ import {
ActionException,
ActionExceptionKind,
allocateProtection,
assertThrowableIsError,
ConstantPeriodBatch,
describeProtection,
EDStatic,
@@ -136,7 +137,7 @@ async function fetchNews(newsURL: string): Promise<Result<DraupnirNewsBlob>> {
(json) => Value.Decode(DraupnirNewsBlob, json),
(error: unknown) =>
ActionException.Result("unable to fetch news", {
exception: error,
exception: assertThrowableIsError(error),
exceptionKind: ActionExceptionKind.Unknown,
})
);
@@ -14,6 +14,7 @@ import ManagementRoomOutput from "../managementroom/ManagementRoomOutput";
import { MatrixSendClient } from "matrix-protection-suite-for-matrix-bot-sdk";
import {
ActionExceptionKind,
assertThrowableIsError,
RoomUpdateError,
RoomUpdateException,
} from "matrix-protection-suite";
@@ -157,7 +158,7 @@ export class EventRedactionQueue {
const error = new RoomUpdateException(
MatrixRoomReference.fromRoomID(redaction.roomID),
ActionExceptionKind.Unknown,
e,
assertThrowableIsError(e),
message
);
errors.push(error);
+7 -3
View File
@@ -23,6 +23,7 @@ import {
Ok,
RoomEvent,
Task,
assertThrowableIsError,
isError,
} from "matrix-protection-suite";
@@ -120,7 +121,10 @@ export class ReportPoller {
(exception: unknown) =>
ActionException.Result(
`Failed to retrieve the context for an event ${report.event_id}`,
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
if (isError(eventContext)) {
@@ -159,7 +163,7 @@ export class ReportPoller {
} catch (e) {
const error = new ActionException(
ActionExceptionKind.Unknown,
e,
assertThrowableIsError(e),
"Unable to set account data for report poller token"
);
await this.draupnir.managementRoomOutput.logMessage(
@@ -179,7 +183,7 @@ export class ReportPoller {
} catch (e) {
const error = new ActionException(
ActionExceptionKind.Unknown,
e,
assertThrowableIsError(e),
"failed to get abuse reports"
);
await this.draupnir.managementRoomOutput.logMessage(
@@ -82,7 +82,7 @@ describe("Test: Report polling", function (this: Mocha.Suite) {
await reportEvent();
}
// wait for them to come down the poll.
await new Promise((resolve) => setTimeout(resolve, 3000));
await new Promise((resolve) => setTimeout(resolve, 5000));
expect(reportsFound.size).toBe(20);
expect(duplicateReports.size).toBe(0);
} as unknown as Mocha.AsyncFunc);
@@ -1,8 +1,7 @@
// SPDX-FileCopyrightText: 2024 Gnuxie <Gnuxie@protonmail.com>
// SPDX-FileCopyrightText: 2024 - 2026 Gnuxie <Gnuxie@protonmail.com>
//
// SPDX-License-Identifier: AFL-3.0
import { StaticDecode, Type } from "@sinclair/typebox";
import { MatrixError } from "@vector-im/matrix-bot-sdk";
import {
ActionException,
@@ -54,17 +53,12 @@ import {
} from "@the-draupnir-project/matrix-basic-types";
import { resolveRoomReferenceSafe } from "../SafeMatrixClient";
import { ResultError } from "@gnuxie/typescript-result";
import util from "util";
import { RoomReactionSender } from "matrix-protection-suite/dist/Client/RoomReactionSender";
import { RoomEventGetter } from "matrix-protection-suite/dist/Client/RoomEventGetter";
import { BotSDKJunkError, toMatrixJunkError } from "./BotSDKJunkErrors";
const log = new Logger("BotSDKBaseClient");
const WeakError = Type.Object({
message: Type.String(),
name: Type.String(),
});
function toRoomID(room: MatrixRoomID | StringRoomID): StringRoomID {
return typeof room === "string" ? room : room.toRoomIDOrAlias();
}
@@ -80,39 +74,17 @@ function matrixExceptionFromMatrixError(
});
}
function actionExceptionFromWeakError(
error: StaticDecode<typeof WeakError>
): ActionResult<never, ActionException> {
return ActionException.Result(error.message, {
function matrixExceptionFromMatrixJunkError(
error: BotSDKJunkError
): ActionResult<never, MatrixException> {
return MatrixException.R({
exception: error,
exceptionKind: ActionExceptionKind.Unknown,
matrixErrorCode: error.matrixErrorCode,
matrixErrorMessage: error.matrixErrorMessage,
message: error.message,
});
}
function unknownError(error: unknown): never {
const printedError = (() => {
if (typeof error === "object" && error !== null) {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const toString = error.toString();
if (toString !== "[object Object]") {
return toString;
}
}
try {
return JSON.stringify(error);
} catch {
return util.inspect(error, {
depth: 2,
maxArrayLength: 10,
breakLength: 80,
});
}
})();
throw new TypeError(
`What on earth are you throwing exactly? because it isn't an error: ${printedError}`
);
}
/**
* This is a utility to extract the raw matrix event from a wrapper type
* that vector bot-sdk decided to use inappropriately.
@@ -154,25 +126,31 @@ export function resultifyBotSDKRequestErrorWith404AsUndefined(
if (is404(error)) {
return Ok(undefined);
}
if (error instanceof MatrixError) {
return matrixExceptionFromMatrixError(error);
} else if (Value.Check(WeakError, error)) {
return actionExceptionFromWeakError(error);
} else {
unknownError(error);
const coercedError = toMatrixJunkError(error);
if (coercedError instanceof MatrixError) {
return matrixExceptionFromMatrixError(coercedError);
} else if (coercedError instanceof BotSDKJunkError) {
return matrixExceptionFromMatrixJunkError(coercedError);
}
return ActionException.Result(coercedError.message, {
exception: coercedError,
exceptionKind: ActionExceptionKind.Unknown,
});
}
export function resultifyBotSDKRequestError(
error: unknown
): ActionResult<never, ActionException> {
if (error instanceof MatrixError) {
return matrixExceptionFromMatrixError(error);
} else if (Value.Check(WeakError, error)) {
return actionExceptionFromWeakError(error);
} else {
unknownError(error);
const coercedError = toMatrixJunkError(error);
if (coercedError instanceof MatrixError) {
return matrixExceptionFromMatrixError(coercedError);
} else if (coercedError instanceof BotSDKJunkError) {
return matrixExceptionFromMatrixJunkError(coercedError);
}
return ActionException.Result(coercedError.message, {
exception: coercedError,
exceptionKind: ActionExceptionKind.Unknown,
});
}
export class BotSDKBaseClient
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2026 Gnuxie <Gnuxie@protonmail.com>
//
// SPDX-License-Identifier: Apache-2.0
import { Type } from "@sinclair/typebox";
import { MatrixError } from "@vector-im/matrix-bot-sdk";
import { Value, assertThrowableIsError } from "matrix-protection-suite";
const MatrixErrorBody = Type.Object({
errcode: Type.String(),
error: Type.String(),
retry_after_ms: Type.Optional(Type.Number()),
});
// The matrix-bot-sdk appservice code sometimes throws junk like `{ body: result }`
// for example in`Intent.ensureRegistered()`.
const MatrixErrorContainer = Type.Object({
body: MatrixErrorBody,
statusCode: Type.Optional(Type.Number()),
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
});
/**
* This exists because the matrix-bot-sdk and matrix-appservice-bridge have
* various inconsistent ways of throwing errors. And Draupnir's own code
* probably makes matters worse in utils.ts. So this is a wrapper for all of
* the undesirable junk that gets thrown. Eventually we want to replace as
* many bot-sdk apis as possible with the matrix-protection-suite ClientPlatform
* an also then implement those apis using fetch.
*/
export class BotSDKJunkError extends Error {
public constructor(
public readonly raw: unknown,
public readonly matrixErrorCode: string,
public readonly matrixErrorMessage: string
) {
super(
`matrix-bot-sdk threw a non-Error matrix payload: ${matrixErrorCode}: ${matrixErrorMessage}`
);
}
}
export function toMatrixJunkError(error: unknown): BotSDKJunkError | Error {
if (error instanceof MatrixError) {
return error;
}
if (Value.Check(MatrixErrorContainer, error)) {
return new BotSDKJunkError(error, error.body.errcode, error.body.error);
}
if (Value.Check(MatrixErrorBody, error)) {
return new BotSDKJunkError(error, error.errcode, error.error);
}
return assertThrowableIsError(error);
}
@@ -9,6 +9,7 @@ import {
ClientRooms,
ClientsInRoomMap,
Ok,
assertThrowableIsError,
} from "matrix-protection-suite";
import { MatrixSendClient } from "../MatrixEmitter";
import {
@@ -34,7 +35,7 @@ export async function joinedRoomsSafe(
(rooms) => Ok(rooms as StringRoomID[]),
(exception: unknown) =>
ActionException.Result(`Unable to get joined rooms`, {
exception,
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
})
);
@@ -7,6 +7,7 @@ import {
ActionExceptionKind,
JoinedRoomsSafe,
Ok,
assertThrowableIsError,
} from "matrix-protection-suite";
import { MatrixSendClient } from "../MatrixEmitter";
import { StringRoomID } from "@the-draupnir-project/matrix-basic-types";
@@ -22,7 +23,7 @@ export function makeJoinedRoomsSafe(
ActionException.Result(
`Unable to fetch the joined rooms for ${clientUserID}`,
{
exception,
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
@@ -12,6 +12,7 @@ import {
PersistentConfigBackend,
RoomStateRevisionIssuer,
Value,
assertThrowableIsError,
isError,
} from "matrix-protection-suite";
import { MatrixSendClient } from "../MatrixEmitter";
@@ -74,7 +75,10 @@ export class BotSDKMatrixAccountData<T> implements MatrixAccountData<T> {
? Ok(undefined)
: ActionException.Result(
`Encountered an error when requesting matrix account data ${this.eventType}`,
{ exception: error, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(error),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
}
@@ -91,7 +95,7 @@ export class BotSDKMatrixAccountData<T> implements MatrixAccountData<T> {
ActionException.Result(
`Unable to store matrix account data ${this.eventType}`,
{
exception,
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
@@ -162,7 +166,7 @@ export class BotSDKMatrixStateData<T> implements MatrixStateData<T> {
ActionException.Result(
`Unable to store the matrix state data ${this.eventType}`,
{
exception,
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Known,
}
)
@@ -19,6 +19,7 @@ import {
ActionException,
ActionExceptionKind,
Ok,
assertThrowableIsError,
isError,
PolicyRuleEvent,
isPolicyRuleEvent,
@@ -96,7 +97,10 @@ export class BotSDKPolicyRoomManager implements PolicyRoomManager {
(exception: unknown) =>
ActionException.Result(
"Could not create a list because we could not find the mxid of the list creator.",
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
if (isError(creator)) {
@@ -173,7 +177,10 @@ export class BotSDKPolicyRoomManager implements PolicyRoomManager {
(exception: unknown) =>
ActionException.Result(
"Could not create a matrix room to serve as the new policy list.",
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
}
@@ -12,6 +12,7 @@ import {
Value,
isError,
Ok,
assertThrowableIsError,
} from "matrix-protection-suite";
import { MatrixSendClient } from "../MatrixEmitter";
@@ -29,7 +30,10 @@ export class BotSDKMjolnirProtectedRoomsStore implements PersistentMatrixData<
(exception: unknown) =>
ActionException.Result(
`Unable to load the account data for mjolnir protected_rooms`,
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
}
@@ -47,7 +51,10 @@ export class BotSDKMjolnirProtectedRoomsStore implements PersistentMatrixData<
(exception: unknown) =>
ActionException.Result(
`Unable to set account data for mjolnir protected_rooms event`,
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
}
@@ -12,6 +12,7 @@ import {
Ok,
MjolnirWatchedPolicyRoomsEvent,
MJOLNIR_WATCHED_POLICY_ROOMS_EVENT_TYPE,
assertThrowableIsError,
} from "matrix-protection-suite";
import { MatrixSendClient } from "../MatrixEmitter";
@@ -31,7 +32,10 @@ export class BotSDKMjolnirWatchedPolicyRoomsStore implements PersistentMatrixDat
(exception: unknown) =>
ActionException.Result(
`Unable to load the account data for mjolnir watched_lists`,
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
}
@@ -49,7 +53,10 @@ export class BotSDKMjolnirWatchedPolicyRoomsStore implements PersistentMatrixDat
(exception: unknown) =>
ActionException.Result(
`Unable to set account data for mjolnir watched_lists`,
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
}
@@ -12,6 +12,7 @@ import {
RoomMembershipManager,
RoomMembershipRevisionIssuer,
Value,
assertThrowableIsError,
isError,
} from "matrix-protection-suite";
import { MembershipEvent } from "matrix-protection-suite";
@@ -40,7 +41,10 @@ async function getRoomMembershipEvents(
(exception: unknown) =>
ActionException.Result(
`Unable to query room members from ${room.toPermalink()}`,
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
if (isError(rawMembersResult)) {
@@ -19,6 +19,7 @@ import {
SynapseAdminPostUserDeactivateRequest,
SynapseReport,
Value,
assertThrowableIsError,
isError,
} from "matrix-protection-suite";
import { MatrixSendClient } from "../MatrixEmitter";
@@ -76,7 +77,10 @@ export class SynapseAdminClient {
(exception: unknown) =>
ActionException.Result(
`Unable to query whether the user ${this.clientUserID} is a Synapse Admin`,
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
if (isError(response)) {
@@ -107,7 +111,10 @@ export class SynapseAdminClient {
(exception: unknown) =>
ActionException.Result(
`Unable to deactivate the user ${targetUserID}`,
{ exception, exceptionKind: ActionExceptionKind.Unknown }
{
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
);
}
@@ -127,7 +134,7 @@ export class SynapseAdminClient {
(_) => Ok(undefined),
(exception: unknown) =>
ActionException.Result(`Unable to delete the room ${roomID}`, {
exception,
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
})
);
@@ -155,7 +162,7 @@ export class SynapseAdminClient {
ActionException.Result(
`Unable to make the user ${userID} admin in room ${roomID}`,
{
exception,
exception: assertThrowableIsError(exception),
exceptionKind: ActionExceptionKind.Unknown,
}
)
@@ -5,6 +5,7 @@
export * from "./Client/BotSDKAllClient";
export * from "./Client/BotSDKBaseClient";
export * from "./Client/BotSDKClientPlatform";
export * from "./Client/BotSDKJunkErrors";
export * from "./ClientManagement/ClientCapabilityFactory";
export * from "./ClientManagement/ClientManagement";
@@ -26,6 +26,41 @@ export enum ActionExceptionKind {
Unknown = "Unknown",
}
function describeThrowable(throwable: unknown): string {
if (typeof throwable === "string") {
return throwable;
}
if (
typeof throwable === "number" ||
typeof throwable === "boolean" ||
typeof throwable === "bigint" ||
typeof throwable === "symbol" ||
throwable === null ||
throwable === undefined
) {
return String(throwable);
}
if (Array.isArray(throwable)) {
return `array(length=${throwable.length})`;
}
if (typeof throwable === "object") {
const keys = Object.keys(throwable);
return keys.length === 0
? "object with no enumerable keys"
: `object with keys: ${keys.join(",")}`;
}
return typeof throwable;
}
export function assertThrowableIsError(throwable: unknown): Error {
if (throwable instanceof Error) {
return throwable;
}
throw new TypeError(
`Expected a thrown Error instance but received ${describeThrowable(throwable)}`
);
}
// TODO: I wonder if we could allow message to be JSX?
/**
* `ActionExceptions` are used to convert throwables into `ActionError`s.
@@ -39,8 +74,7 @@ export class ActionException extends ActionError {
constructor(
public readonly exceptionKind: ActionExceptionKind,
// make a call to only allow Error in a moment.
public readonly exception: unknown,
public readonly exception: Error,
message: string,
{
uuid = randomUUID(),
@@ -69,7 +103,7 @@ export class ActionException extends ActionError {
*/
public static Result(
message: string,
options: { exception: unknown; exceptionKind: ActionExceptionKind }
options: { exception: Error; exceptionKind: ActionExceptionKind }
): ActionResult<never, ActionException> {
return ResultError(
new ActionException(options.exceptionKind, options.exception, message)
@@ -93,11 +127,6 @@ export class ActionException extends ActionError {
public toReadableString(): string {
const mainDetail = `ActionException: ${this.uuid}\n${super.toReadableString()}`;
if (this.exception instanceof Error) {
return `${mainDetail}\nfrom error: ${this.exception.name}: ${this.exception.message}\n${this.exception.stack}`;
}
// @typescript-eslint/restrict-template-expressions
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${mainDetail}\nfrom unknown: ${this.exception}`;
return `${mainDetail}\nfrom error: ${this.exception.name}: ${this.exception.message}\n${this.exception.stack}`;
}
}
@@ -7,7 +7,7 @@ import { ActionException, ActionExceptionKind } from "./ActionException";
export class MatrixException extends ActionException implements ActionError {
public constructor(
exception: unknown,
exception: Error,
public readonly matrixErrorCode: string,
public readonly matrixErrorMessage: string,
message: string = matrixErrorMessage,
@@ -30,7 +30,7 @@ export class MatrixException extends ActionException implements ActionError {
}): ActionResult<never, MatrixException> {
return ResultError(
new MatrixException(
options.exceptionKind,
options.exception,
options.matrixErrorCode,
options.matrixErrorMessage,
options.message,
@@ -5,7 +5,11 @@
import { ResultError } from "@gnuxie/typescript-result";
import { Logger } from "../Logging/Logger";
import { ActionError } from "./Action";
import { ActionException, ActionExceptionKind } from "./ActionException";
import {
ActionException,
ActionExceptionKind,
assertThrowableIsError,
} from "./ActionException";
const log = new Logger("Task");
@@ -67,7 +71,7 @@ export async function Task(
} catch (exception) {
const actionException = new ActionException(
ActionExceptionKind.Unknown,
exception,
assertThrowableIsError(exception),
"A Task failed with an unknown exception"
);
globalTaskReporter(actionException, options);
@@ -16,14 +16,18 @@ import {
ValueErrorIterator,
} from "@sinclair/typebox/compiler";
import { ActionResult, Ok, ResultError } from "./Action";
import { ActionException, ActionExceptionKind } from "./ActionException";
import {
ActionException,
ActionExceptionKind,
assertThrowableIsError,
} from "./ActionException";
import { Logger } from "../Logging/Logger";
export class DecodeException extends ActionException {
private static log = new Logger("DecodeException");
constructor(
message: string,
exception: unknown,
exception: Error,
public readonly errors: ValueError[],
suppressLog?: boolean
) {
@@ -108,7 +112,7 @@ export class Value {
throw e;
} else {
return ActionException.Result(`Unable to decode schema from value`, {
exception: e,
exception: assertThrowableIsError(e),
exceptionKind: ActionExceptionKind.Unknown,
});
}