log AnonymousError context detail in serialized output

Surface the path/repo/url tied to an AnonymousError when it gets
serialized for logging — previously logs only carried name, message,
and httpStatus, which made file_not_found entries impossible to trace
back to a specific file or repo. Extract the existing detail formatting
out of toString() into a public detail() method, harden it against
AnonymizedFile getters that can throw, and have serializeError include
the result as a "detail" field.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
tdurieux
2026-05-06 13:46:06 +03:00
parent 7dc48bccd2
commit 6f418d6332
2 changed files with 39 additions and 14 deletions
+30 -14
View File
@@ -27,21 +27,37 @@ export default class AnonymousError extends CustomError {
this.cause = opt?.cause;
}
toString(): string {
let out = "";
let detail = this.value ? JSON.stringify(this.value) : null;
if (this.value instanceof Repository) {
detail = this.value.repoId;
} else if (this.value instanceof AnonymizedFile) {
detail = `/r/${this.value.repository.repoId}/${this.value.anonymizedPath}`;
} else if (this.value instanceof GitHubRepository) {
detail = `${this.value.fullName}`;
} else if (this.value instanceof User) {
detail = `${this.value.username}`;
} else if (this.value instanceof GitHubBase) {
detail = `GHDownload ${this.value.data.repoId}`;
detail(): string | undefined {
if (this.value == null) return undefined;
try {
if (this.value instanceof Repository) return this.value.repoId;
if (this.value instanceof AnonymizedFile) {
const repoId = this.value.repository?.repoId;
// anonymizedPath getter can throw if the file isn't initialized;
// fall back to whatever path is known.
let p: string | undefined;
try {
p = this.value.anonymizedPath;
} catch {
p = this.value.filePath;
}
return repoId ? `/r/${repoId}/${p ?? ""}` : p;
}
if (this.value instanceof GitHubRepository) return this.value.fullName;
if (this.value instanceof User) return this.value.username;
if (this.value instanceof GitHubBase) {
return `GHDownload ${this.value.data.repoId}`;
}
if (typeof this.value === "string") return this.value;
return JSON.stringify(this.value);
} catch {
return String(this.value);
}
out += this.message;
}
toString(): string {
let out = this.message;
const detail = this.detail();
if (detail) {
out += `: ${detail}`;
}
+9
View File
@@ -140,6 +140,7 @@ type ErrorLike = {
cause?: unknown;
request?: { url?: string; method?: string };
response?: { url?: string; status?: number };
detail?: () => string | undefined;
};
export function serializeError(err: unknown): Record<string, unknown> {
@@ -161,6 +162,14 @@ export function serializeError(err: unknown): Record<string, unknown> {
// AnonymousError carries an httpStatus and an inner cause.
if (typeof e.httpStatus === "number") out.httpStatus = e.httpStatus;
if (e.code !== undefined && e.code !== e.message) out.code = e.code;
if (typeof e.detail === "function") {
try {
const d = e.detail();
if (d) out.detail = d;
} catch {
/* ignore */
}
}
if (e.cause) out.cause = serializeError(e.cause);
// Only include the stack when there's nothing else useful — avoids dumping