Skip to content

Commit 599bbe9

Browse files
breken-aiclaude
andcommitted
fix(core): Resolve escape sequences in fmt / parameterize messages
`parameterize` built the message with `String.raw`, so an escape such as `\n` or `` \` `` stayed as a backslash sequence in the log body or event message, while the template attribute used the cooked strings. A string with an invalid escape sequence (e.g. a Windows path) has no cooked value, so its text dropped out of the template entirely. Build both from the cooked strings, falling back to the raw string where there is no cooked value. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent bd3ce5f commit 599bbe9

2 files changed

Lines changed: 21 additions & 2 deletions

File tree

‎packages/core/src/utils/parameterize.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ import type { ParameterizedString } from '../types/parameterize';
1212
* @returns A `ParameterizedString` object that can be passed into `captureMessage` or Sentry.logger.X methods.
1313
*/
1414
export function parameterize(strings: TemplateStringsArray, ...values: unknown[]): ParameterizedString {
15-
const formatted = new String(String.raw(strings, ...values)) as ParameterizedString;
16-
formatted.__sentry_template_string__ = strings.join('\x00').replace(/%/g, '%%').replace(/\0/g, '%s');
15+
// `String.raw` would keep escape sequences such as `\n` as typed, while the template uses the cooked strings.
16+
// A string with an invalid escape sequence has no cooked value, so fall back to its raw form.
17+
const cooked = strings.map((str, i) => str ?? strings.raw[i]);
18+
const formatted = new String(String.raw({ raw: cooked }, ...values)) as ParameterizedString;
19+
formatted.__sentry_template_string__ = cooked.join('\x00').replace(/%/g, '%%').replace(/\0/g, '%s');
1720
formatted.__sentry_template_values__ = values;
1821
return formatted;
1922
}

‎packages/core/test/lib/utils/parameterize.test.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,20 @@ describe('parameterize()', () => {
2424
expect(formatted.__sentry_template_string__).toEqual(string.__sentry_template_string__);
2525
expect(formatted.__sentry_template_values__).toEqual(string.__sentry_template_values__);
2626
});
27+
28+
test('keeps escape sequences the same in the message and the template', () => {
29+
const x = 'first';
30+
const formatted = parameterize`Line one\nline two with ${x} → \`done\``;
31+
32+
expect(String(formatted)).toBe('Line one\nline two with first → `done`');
33+
expect(formatted.__sentry_template_string__).toBe('Line one\nline two with %s → `done`');
34+
});
35+
36+
test('keeps the raw text of a string with an invalid escape sequence', () => {
37+
const file = 'app.log';
38+
const formatted = parameterize`Reading C:\users ${file}`;
39+
40+
expect(String(formatted)).toBe('Reading C:\\users app.log');
41+
expect(formatted.__sentry_template_string__).toBe('Reading C:\\users %s');
42+
});
2743
});

0 commit comments

Comments
 (0)