operationErrorMessage in src/integrations/api/instance/secrets/secrets.ts:58 lifts Harper's response-body text into a new Error(...):
function operationErrorMessage(error: unknown): string {
const body = (error as { response?: { data?: { error?: string; message?: string } } })?.response?.data;
return body?.error ?? body?.message ?? String(error);
}
Three call sites throw it (createSecret, updateSecret, deleteSecret). Those rejections reach React Query's global handler, which calls console.error, and the RUM SDK reports a console.error as an error event — so whatever Harper composed for a failed secrets operation is published to Datadog Error Tracking, where it is retained and searchable.
Why the existing redaction doesn't cover it
beforeSend redacts URL- and scp-shaped tokens out of error text, and (as of the fix referenced below) withholds the message of a relayed error entirely. That withholding keys on error.type, which RUM takes from the thrown value's name. These are plain new Error(...), so they arrive as type: "Error" and fall outside the rule.
Harper composes those operation errors by interpolation, the same way it composes the deploy failures where this class was first found — so the text can carry a name, path, or identifier the customer supplied, in whatever shape they supplied it. Free text with spaces is not URL-shaped and no bounded redaction rule spans it.
Suggested direction
Give the relay a type the filter can see, rather than adding another message-shape rule: throw a named error class (mirroring SSEOperationError) from operationErrorMessage's call sites and add it to RELAYED_ERROR_TYPES. That reuses the mechanism already in place and keeps the "is this text ours?" decision at the throw site, where it is known.
Scale
Zero events in the last 7 days of production RUM — the path fires only when a secrets operation fails. It is filed as a latent exposure found while fixing the same class on the deploy path, not as an active leak.
Related
- Fixed on the SSE deploy path by the PR linked below.
- Found by the cross-model pre-push review of that PR, which flagged it as a pre-existing sibling outside the diff.
operationErrorMessageinsrc/integrations/api/instance/secrets/secrets.ts:58lifts Harper's response-body text into anew Error(...):Three call sites throw it (
createSecret,updateSecret,deleteSecret). Those rejections reach React Query's global handler, which callsconsole.error, and the RUM SDK reports aconsole.erroras an error event — so whatever Harper composed for a failed secrets operation is published to Datadog Error Tracking, where it is retained and searchable.Why the existing redaction doesn't cover it
beforeSendredacts URL- and scp-shaped tokens out of error text, and (as of the fix referenced below) withholds the message of a relayed error entirely. That withholding keys onerror.type, which RUM takes from the thrown value'sname. These are plainnew Error(...), so they arrive astype: "Error"and fall outside the rule.Harper composes those operation errors by interpolation, the same way it composes the deploy failures where this class was first found — so the text can carry a name, path, or identifier the customer supplied, in whatever shape they supplied it. Free text with spaces is not URL-shaped and no bounded redaction rule spans it.
Suggested direction
Give the relay a type the filter can see, rather than adding another message-shape rule: throw a named error class (mirroring
SSEOperationError) fromoperationErrorMessage's call sites and add it toRELAYED_ERROR_TYPES. That reuses the mechanism already in place and keeps the "is this text ours?" decision at the throw site, where it is known.Scale
Zero events in the last 7 days of production RUM — the path fires only when a secrets operation fails. It is filed as a latent exposure found while fixing the same class on the deploy path, not as an active leak.
Related