mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: error-only Sentry telemetry, storm-proof capture, and crash fixes (#476)
Removes Sentry tracing entirely (BullMQ idle polling burned 4.8M transactions in 2 days at the baked 0.1 rate), decouples PostHog sampling, and replaces the type-only error scrub with a vetted-field sanitizer plus SafeError/ToolInputError contracts. One classified capture path with per-signature throttles and a per-process ceiling makes storms impossible (NODE-1E was 4,541 events from one 30s loop). Browser errors move to a dedicated web Sentry project with their own source maps. Adds the SNAPOTTER_TELEMETRY runtime kill switch and silences test fleets. Crash fixes: remote 204/304 SSRF process kill (NODE-20), conversion-preset boot crash loop (NODE-21), Redis version preflight + unhandled subscribe rejection (NODE-1T), Sign PDF on plain-http origins (NODE-1K/1M), wavesurfer/pdf.js teardown rejections (NODE-1P/1N), bundle-import ZlibError to 400 (NODE-1Z), chart-maker input errors declassified (NODE-1H/1J), asset requests skip the session DB lookup (NODE-1D).
This commit is contained in:
+25
-12
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { context, propagation, SpanStatusCode, trace } from "@opentelemetry/api";
|
||||
import { SafeError } from "@snapotter/shared";
|
||||
import { missingBundleForScript } from "./feature-gate.js";
|
||||
import { acquireVenvRead, tryAcquireVenvRead } from "./venv-lock.js";
|
||||
|
||||
@@ -327,17 +328,24 @@ export class PythonDispatcher {
|
||||
if (req) {
|
||||
this.pending.delete(reqId);
|
||||
if (response.exitCode !== 0) {
|
||||
const errText =
|
||||
extractPythonError({
|
||||
stdout: response.stdout,
|
||||
stderr: req.stderrLines.join("\n"),
|
||||
}) ||
|
||||
(response.exitCode === 137
|
||||
? "Process killed (out of memory) -- try a lighter model or smaller image"
|
||||
: response.exitCode === 139
|
||||
? "Process crashed (segmentation fault)"
|
||||
: `Python script exited with code ${response.exitCode}`);
|
||||
req.reject(new Error(errText));
|
||||
const extracted = extractPythonError({
|
||||
stdout: response.stdout,
|
||||
stderr: req.stderrLines.join("\n"),
|
||||
});
|
||||
if (!extracted && (response.exitCode === 137 || response.exitCode === 139)) {
|
||||
req.reject(
|
||||
new SafeError(
|
||||
response.exitCode === 137
|
||||
? "Process killed (out of memory) -- try a lighter model or smaller image"
|
||||
: "Process crashed (segmentation fault)",
|
||||
{ kind: "operational", code: `exit-${response.exitCode}` },
|
||||
),
|
||||
);
|
||||
} else {
|
||||
req.reject(
|
||||
new Error(extracted || `Python script exited with code ${response.exitCode}`),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
req.resolve({
|
||||
stdout: response.stdout || "",
|
||||
@@ -564,8 +572,13 @@ export class PythonDispatcher {
|
||||
: signal === "SIGSEGV" || code === 139
|
||||
? "Process crashed (segmentation fault)"
|
||||
: null;
|
||||
if (signalMsg) {
|
||||
rejectPromise(
|
||||
new SafeError(signalMsg, { kind: "operational", code: `exit-${code ?? signal}` }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
const errorText =
|
||||
signalMsg ||
|
||||
extractPythonError({ stdout: stdout.trim(), stderr }) ||
|
||||
`Python script exited with code ${code}`;
|
||||
rejectPromise(new Error(errorText));
|
||||
|
||||
Reference in New Issue
Block a user