mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 07:19:52 +00:00
fix(cli): report transcribe failure reasons via cli_error (command_error) (#1479)
Transcribe failures are recorded as `cli_command_result success=false` but
without a reason: the command catches its own error, prints it, and
`process.exit(1)` — the message never reaches telemetry. `cli_error` was only
emitted from the uncaughtException / unhandledRejection handlers, so
self-handled command failures were invisible. That makes a high failure rate
countable but not debuggable.
Add `trackCommandFailure(command, err)` — a thin wrapper over the existing
`trackCliError({ kind: "command_error" })` that normalizes an unknown reason to
name/message/stack. It enqueues synchronously, so the process `exit` handler's
flushSync ships it alongside `cli_command_result`. Respects the telemetry
opt-out (gated in trackEvent) and reuses the existing PII redaction.
Wire it into all three of transcribe's failure exits (file-not-found,
empty-transcript import, and the transcribe() catch — ffmpeg / whisper-binary /
model-download errors). Now each failure carries its reason, so we can see how
much of the failure rate is environment vs user input.
The helper is generic — the same one-liner can be dropped into other commands'
failure paths, or centralized at the runMain boundary, as a follow-up.
This commit is contained in:
@@ -5,7 +5,8 @@ vi.mock("./client.js", () => ({
|
||||
trackEvent: (...args: unknown[]) => trackEvent(...args),
|
||||
}));
|
||||
|
||||
const { trackRenderError, trackRenderObservation } = await import("./events.js");
|
||||
const { trackRenderError, trackRenderObservation, trackCommandFailure } =
|
||||
await import("./events.js");
|
||||
|
||||
describe("render telemetry events", () => {
|
||||
beforeEach(() => {
|
||||
@@ -50,3 +51,38 @@ describe("render telemetry events", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("trackCommandFailure", () => {
|
||||
beforeEach(() => {
|
||||
trackEvent.mockClear();
|
||||
});
|
||||
|
||||
it("reports an Error as a command_error with name/message/stack", () => {
|
||||
const err = new Error("ffmpeg is required to extract audio");
|
||||
trackCommandFailure("transcribe", err);
|
||||
|
||||
expect(trackEvent).toHaveBeenCalledWith(
|
||||
"cli_error",
|
||||
expect.objectContaining({
|
||||
kind: "command_error",
|
||||
command: "transcribe",
|
||||
error_name: "Error",
|
||||
error_message: "ffmpeg is required to extract audio",
|
||||
stack_trace: err.stack,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("coerces a non-Error reason (e.g. a string) into the message", () => {
|
||||
trackCommandFailure("transcribe", "No words found in transcript.");
|
||||
|
||||
expect(trackEvent).toHaveBeenCalledWith(
|
||||
"cli_error",
|
||||
expect.objectContaining({
|
||||
kind: "command_error",
|
||||
command: "transcribe",
|
||||
error_message: "No words found in transcript.",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -284,6 +284,21 @@ export function trackCliError(props: {
|
||||
});
|
||||
}
|
||||
|
||||
// Report why a command failed before it exits non-zero. cli_command_result
|
||||
// records the failure but not the reason; this fills that gap via cli_error so
|
||||
// command failures are diagnosable. Enqueues synchronously — the process `exit`
|
||||
// handler flushes it. Drop this into any command's failure path.
|
||||
export function trackCommandFailure(command: string, err: unknown): void {
|
||||
const error = err instanceof Error ? err : new Error(String(err));
|
||||
trackCliError({
|
||||
error_name: error.name,
|
||||
error_message: error.message,
|
||||
stack_trace: error.stack,
|
||||
command,
|
||||
kind: "command_error",
|
||||
});
|
||||
}
|
||||
|
||||
export function trackRenderFeedback(props: {
|
||||
rating: number;
|
||||
renderDurationMs: number;
|
||||
|
||||
Reference in New Issue
Block a user