Skip to content

fix(otel): stop base64 media regex from matching across commas - #872

Open
jerichosiahaya wants to merge 1 commit into
langfuse:mainfrom
jerichosiahaya:fix/media-regex-comma-greedy-match
Open

fix(otel): stop base64 media regex from matching across commas#872
jerichosiahaya wants to merge 1 commit into
langfuse:mainfrom
jerichosiahaya:fix/media-regex-comma-greedy-match

Conversation

@jerichosiahaya

@jerichosiahaya jerichosiahaya commented Jul 15, 2026

Copy link
Copy Markdown

MediaService.process() scans span attributes for data URIs with /data:[^;]+;base64,.../g. The mediatype segment [^;]+ excludes only semicolons, so when a trace payload contains a plain-text 'data:' mention earlier in the same string (e.g. user message text), the regex greedily matches from that first 'data:' through unrelated text, including any commas, all the way to a later real ';base64,' data URI. The resulting bogus match fails to parse ('[Langfuse SDK] [ERROR] Error parsing base64 data URI Error: Data is not base64 encoded'), and the real media is silently dropped.

Exclude commas too ([^;,]+): real mediatypes (image/png, application/pdf, ...) never contain a comma, so legitimate matches are unaffected, but the regex can no longer span past one.

Problem

Changes

Verification

  • pnpm lint
  • pnpm typecheck
  • pnpm test
  • pnpm test:integration
  • pnpm test:e2e
  • pnpm format:check

Release info

Bump level

  • Major
  • Minor
  • Patch

Libraries affected

  • All of them
  • @langfuse/core
  • @langfuse/client
  • @langfuse/tracing
  • @langfuse/otel
  • @langfuse/openai
  • @langfuse/langchain

Changelog notes

  • Added support for X

Greptile Summary

This PR fixes a greedy-regex bug in MediaService.process() where a plain-text data: mention earlier in a span attribute string could be stitched to a later, real data:URI by the extraction regex, producing a bogus match that failed base64 parsing and silently dropped the real media. The one-character change ([^;]+[^;,]+) prevents cross-comma greedy matching; MIME types never contain commas, so no valid data URI is affected.

  • Regex fix (packages/otel/src/MediaService.ts): adds , to the excluded character set in the mediatype segment, so the regex anchors tightly and cannot span past a comma into unrelated text.
  • Tests (tests/unit/mediaService.test.ts): adds three unit tests covering a normal data URI extraction, the regression scenario (plain-text data: followed by a real URI in the same string), and the no-URI-present path.

Confidence Score: 5/5

This is a minimal, targeted regex fix in a single character class; the change cannot affect any valid MIME type and directly closes the greedy-match escape hatch that caused bogus URI parsing.

The one-character change is logically sound — MIME types never contain commas, so adding ',' to the negated class only restricts invalid matches. The three new unit tests directly exercise the fixed path, the regression scenario, and the no-URI path, giving good coverage of the changed behavior. No edge cases are introduced or regressed.

No files require special attention. The test mock returns getUploadUrl synchronously (not a Promise), which works fine in JS/TS but differs from the real API shape — worth keeping in mind if the test suite is later extended.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["span attribute value (string)"] --> B["Apply regex\n/data:[^;,]+;base64,[A-Za-z0-9+/]+=*/g"]
    B --> C{Matches found?}
    C -- No --> D[Skip attribute]
    C -- Yes --> E[Deduplicate matches via Set]
    E --> F["For each match: create LangfuseMedia\n(base64DataUri)"]
    F --> G["media.getTag() → langfuseMediaTag"]
    G --> H{Tag created?}
    H -- No --> I[Log warning, skip item]
    H -- Yes --> J[scheduleUpload in background]
    J --> K["Replace raw data URI in attribute\nwith @@@langfuse-media:... tag"]
    K --> L[Update span.attributes key]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["span attribute value (string)"] --> B["Apply regex\n/data:[^;,]+;base64,[A-Za-z0-9+/]+=*/g"]
    B --> C{Matches found?}
    C -- No --> D[Skip attribute]
    C -- Yes --> E[Deduplicate matches via Set]
    E --> F["For each match: create LangfuseMedia\n(base64DataUri)"]
    F --> G["media.getTag() → langfuseMediaTag"]
    G --> H{Tag created?}
    H -- No --> I[Log warning, skip item]
    H -- Yes --> J[scheduleUpload in background]
    J --> K["Replace raw data URI in attribute\nwith @@@langfuse-media:... tag"]
    K --> L[Update span.attributes key]
Loading

Reviews (1): Last reviewed commit: "fix(otel): stop base64 media regex from ..." | Re-trigger Greptile

MediaService.process() scans span attributes for data URIs with
/data:[^;]+;base64,.../g. The mediatype segment [^;]+ excludes only
semicolons, so when a trace payload contains a plain-text 'data:'
mention earlier in the same string (e.g. user message text), the
regex greedily matches from that first 'data:' through unrelated
text, including any commas, all the way to a later real
';base64,' data URI. The resulting bogus match fails to parse
('[Langfuse SDK] [ERROR] Error parsing base64 data URI Error:
Data is not base64 encoded'), and the real media is silently
dropped.

Exclude commas too ([^;,]+): real mediatypes (image/png,
application/pdf, ...) never contain a comma, so legitimate matches
are unaffected, but the regex can no longer span past one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

@jerichosiahaya is attempting to deploy a commit to the langfuse Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@jerichosiahaya

Copy link
Copy Markdown
Author

The bug

The regex that scans trace payloads for base64 file attachments was too permissive:

  • /data:[^;]+;base64,[A-Za-z0-9+/]+=*/g
  • /data:[^;,]+;base64,[A-Za-z0-9+/]+=*/g

[^;]+ means "any character except ;" — which allowed the match to run across commas and unrelated text. If a trace payload contained the literal text data: before a real attached file, the regex would glue the two together into one bogus match, which then failed to parse as a valid data URI and threw:

[Langfuse SDK] [ERROR] Error parsing base64 data URI Error: Data is not base64 encoded

The fix

Added , to the excluded characters. A real file's mediatype (image/png, application/pdf, etc.) never contains a comma, so this closes the false-match path without affecting any legitimate file upload.

What else was added

  • An inline comment above the regex explaining the failure mode, so future readers know why commas are excluded.
  • A regression test (tests/unit/mediaService.test.ts) using the exact repro payload, verified to fail against the old regex and pass against the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants