Skip to content

Commit

Permalink
INN-2152 Slim down "pretty errors" before sending to Inngest (#360)
Browse files Browse the repository at this point in the history
## Summary
<!-- Succinctly describe your change, providing context, what you've
changed, and why. -->

Slims down "pretty errors," whose name is already subjective, before
sending them to Inngest, by stripping ANSI codes and removing immediate
debugging information.

|
![Before](https://github.com/inngest/inngest-js/assets/1736957/5cc6e965-4806-483e-9ae5-c0c38ff5d960)
|
![image](https://github.com/inngest/inngest-js/assets/1736957/8e7f22bf-6f7c-4c80-b8ab-9b0f231a4326)
|
| - | - |
| Before | After |

The UI could access the code being sent (e.g.
`AUTOMATIC_PARALLEL_INDEXING`), but it's probably nicer to send this
code inside serialized errors in the future, allowing the UI to provide
hints and links to documentation, inclusive of short links such as
`https://innge.st/ERR_CODE_HERE`.

## Checklist
<!-- Tick these items off as you progress. -->
<!-- If an item isn't applicable, ideally please strikeout the item by
wrapping it in "~~"" and suffix it with "N/A My reason for skipping
this." -->
<!-- e.g. "- [ ] ~~Added tests~~ N/A Only touches docs" -->

- [ ] ~~Added a [docs PR](https://github.com/inngest/website) that
references this PR~~ N/A Improvement
- [x] Added unit/integration tests
- [x] Added changesets if applicable

## Related
<!-- A space for any related links, issues, or PRs. -->
<!-- Linear issues are autolinked. -->
<!-- e.g. - INN-123 -->
<!-- GitHub issues/PRs can be linked using shorthand. -->
<!-- e.g. "- inngest/inngest#123" -->
<!-- Feel free to remove this section if there are no applicable related
links.-->
- INN-2152
  • Loading branch information
jpwilliams committed Oct 16, 2023
1 parent 6bc91d0 commit 260dd75
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/cold-hairs-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"inngest": patch
---

Inngest errors now appear more succintly in UIs, free of ANSI codes and verbose information
1 change: 1 addition & 0 deletions packages/inngest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
"json-stringify-safe": "^5.0.1",
"ms": "^2.1.3",
"serialize-error-cjs": "^0.1.3",
"strip-ansi": "^5.2.0",
"type-fest": "^3.13.1",
"zod": "~3.22.3"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/inngest/src/components/execution/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { logPrefix } from "../../helpers/consts";
import {
ErrCode,
deserializeError,
minifyPrettyError,
prettyError,
serializeError,
} from "../../helpers/errors";
Expand Down Expand Up @@ -459,7 +460,7 @@ class V1InngestExecution extends InngestExecution implements IInngestExecution {
retriable = error.retryAfter;
}

const serializedError = serializeError(error);
const serializedError = minifyPrettyError(serializeError(error));

return { type: "function-rejected", error: serializedError, retriable };
}
Expand Down
64 changes: 63 additions & 1 deletion packages/inngest/src/helpers/errors.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { isSerializedError, serializeError } from "@local/helpers/errors";
import {
ErrCode,
fixEventKeyMissingSteps,
isSerializedError,
minifyPrettyError,
prettyError,
serializeError,
} from "@local/helpers/errors";

interface ErrorTests {
name: string;
Expand Down Expand Up @@ -103,3 +110,58 @@ describe("serializeError", () => {
tests: { message: "test" },
});
});

describe("minifyPrettyError", () => {
describe("should minify a pretty error", () => {
const originalErr = serializeError(
new Error(
prettyError({
whatHappened: "Failed to send event",
consequences: "Your event or events were not sent to Inngest.",
why: "We couldn't find an event key to use to send events to Inngest.",
toFixNow: fixEventKeyMissingSteps,
})
)
);

const err = minifyPrettyError(originalErr);

const expected = "Failed to send event";

test("sets message", () => {
expect(err.message).toBe(expected);
});

test("sets stack", () => {
expect(err.stack).toMatch(`Error: ${expected}\n`);
expect(err.stack).toMatch(originalErr.stack);
});
});

describe("should prepend code", () => {
const originalErr = serializeError(
new Error(
prettyError({
whatHappened: "Failed to send event",
consequences: "Your event or events were not sent to Inngest.",
why: "We couldn't find an event key to use to send events to Inngest.",
toFixNow: fixEventKeyMissingSteps,
code: ErrCode.NESTING_STEPS,
})
)
);

const err = minifyPrettyError(originalErr);

const expected = `${ErrCode.NESTING_STEPS} - Failed to send event`;

test("sets message", () => {
expect(err.message).toBe(expected);
});

test("sets stack", () => {
expect(err.stack).toMatch(`Error: ${expected}\n`);
expect(err.stack).toMatch(originalErr.stack);
});
});
});
69 changes: 66 additions & 3 deletions packages/inngest/src/helpers/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
errorConstructors,
type SerializedError as CjsSerializedError,
} from "serialize-error-cjs";
import stripAnsi from "strip-ansi";
import { z } from "zod";
import { type Inngest } from "../components/Inngest";
import { NonRetriableError } from "../components/NonRetriableError";
Expand Down Expand Up @@ -274,6 +275,69 @@ export interface PrettyError {
code?: ErrCode;
}

export const prettyErrorSplitter =
"=================================================";

/**
* Given an unknown `err`, mutate it to minify any pretty errors that it
* contains.
*/
export const minifyPrettyError = <T>(err: T): T => {
try {
if (!isError(err)) {
return err;
}

const isPrettyError = err.message.includes(prettyErrorSplitter);
if (!isPrettyError) {
return err;
}

const sanitizedMessage = stripAnsi(err.message);

const message =
sanitizedMessage.split(" ")[1]?.split("\n")[0]?.trim() || err.message;
const code =
sanitizedMessage.split("\n\nCode: ")[1]?.split("\n\n")[0]?.trim() ||
undefined;

err.message = [code, message].filter(Boolean).join(" - ");

if (err.stack) {
const sanitizedStack = stripAnsi(err.stack);
const stackRest = sanitizedStack
.split(`${prettyErrorSplitter}\n`)
.slice(2)
.join("\n");

err.stack = `${err.name}: ${err.message}\n${stackRest}`;
}

return err;
} catch (noopErr) {
return err;
}
};

/**
* Given an `err`, return a boolean representing whether it is in the shape of
* an `Error` or not.
*/
const isError = (err: unknown): err is Error => {
try {
if (err instanceof Error) {
return true;
}

const hasName = Object.prototype.hasOwnProperty.call(err, "name");
const hasMessage = Object.prototype.hasOwnProperty.call(err, "message");

return hasName && hasMessage;
} catch (noopErr) {
return false;
}
};

/**
* Given a {@link PrettyError}, return a nicely-formatted string ready to log
* or throw.
Expand Down Expand Up @@ -302,7 +366,6 @@ export const prettyError = ({
>
)[type];

const splitter = "=================================================";
let header = `${icon} ${chalk.bold.underline(whatHappened.trim())}`;
if (stack) {
header +=
Expand Down Expand Up @@ -333,12 +396,12 @@ export const prettyError = ({
const trailer = [otherwise?.trim()].filter(Boolean).join(" ");

const message = [
splitter,
prettyErrorSplitter,
header,
body,
trailer,
code ? `Code: ${code}` : "",
splitter,
prettyErrorSplitter,
]
.filter(Boolean)
.join("\n\n");
Expand Down
15 changes: 15 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 260dd75

Please sign in to comment.