Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 2 additions & 24 deletions src/sentry/lang/java/processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
from sentry.lang.java.exceptions import Exceptions
from sentry.lang.java.utils import JAVA_PLATFORMS, get_jvm_images, get_proguard_images
from sentry.lang.java.view_hierarchies import ViewHierarchies
from sentry.lang.native.error import SymbolicationFailed, write_error
from sentry.lang.native.symbolicator import FrameOrder, Symbolicator
from sentry.lang.native.symbolicator import FrameOrder, Symbolicator, handle_response_status
from sentry.models.eventerror import EventError
from sentry.models.project import Project
from sentry.models.release import Release
Expand Down Expand Up @@ -105,27 +104,6 @@ def _normalize_frame(raw_frame: dict[str, Any], index: int) -> dict[str, Any]:
return frame


def _handle_response_status(event_data: Any, response_json: dict[str, Any]) -> bool | None:
"""Checks the response from Symbolicator and reports errors.
Returns `True` on success."""

if not response_json:
error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE)
elif response_json["status"] == "completed":
return True
elif response_json["status"] == "failed":
error = SymbolicationFailed(
message=response_json.get("message") or None,
type=EventError.NATIVE_SYMBOLICATOR_FAILED,
)
else:
logger.error("Unexpected symbolicator status: %s", response_json["status"])
error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE)

write_error(error, event_data)
return None


def _get_release_package(project: Project, release_name: str | None) -> str | None:
"""Gets the release package for the given project and release."""

Expand Down Expand Up @@ -225,7 +203,7 @@ def process_jvm_stacktraces(symbolicator: Symbolicator, data: Any) -> Any:
frame_order=FrameOrder.caller_first,
)

if not _handle_response_status(data, response):
if not handle_response_status(data, response):
return

processing_errors = response.get("errors", [])
Expand Down
22 changes: 2 additions & 20 deletions src/sentry/lang/javascript/processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

from sentry.debug_files.artifact_bundles import maybe_renew_artifact_bundles_from_processing
from sentry.lang.javascript.utils import JAVASCRIPT_PLATFORMS
from sentry.lang.native.error import SymbolicationFailed, write_error
from sentry.lang.native.symbolicator import FrameOrder, Symbolicator
from sentry.lang.native.symbolicator import FrameOrder, Symbolicator, handle_response_status
from sentry.models.eventerror import EventError
from sentry.stacktraces.processing import find_stacktraces_in_data
from sentry.utils import metrics
Expand Down Expand Up @@ -74,23 +73,6 @@ def _merge_frame(new_frame, symbolicated):
return new_frame


# TODO: Change this error handling to be JS-specific?
def _handle_response_status(event_data, response_json):
if not response_json:
error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE)
elif response_json["status"] == "completed":
return True
elif response_json["status"] == "failed":
error = SymbolicationFailed(
message=response_json.get("message") or None, type=EventError.NATIVE_SYMBOLICATOR_FAILED
)
else:
logger.error("Unexpected symbolicator status: %s", response_json["status"])
error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE)

write_error(error, event_data)


def is_sourcemap_image(image):
return (
bool(image)
Expand Down Expand Up @@ -257,7 +239,7 @@ def process_js_stacktraces(symbolicator: Symbolicator, data: Any) -> Any:
frame_order=FrameOrder.caller_first,
)

if not _handle_response_status(data, response):
if not handle_response_status(data, response):
return data

used_artifact_bundles = response.get("used_artifact_bundles", [])
Expand Down
7 changes: 6 additions & 1 deletion src/sentry/lang/native/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@


class SymbolicationFailed(Exception):
def __init__(self, message=None, type=None, obj=None):
def __init__(self, message=None, type=None, event_id=None, obj=None):
Exception.__init__(self)
self.message = str(message)
self.type = type
self.event_id = event_id
self.image_name: str | None = None
self.image_path: str | None = None
if obj is not None:
Expand Down Expand Up @@ -68,6 +69,8 @@ def is_sdk_failure(self):
def get_data(self):
"""Returns the event data."""
rv = {"message": self.message, "type": self.type}
if self.event_id is not None:
rv["sentry_event_id"] = self.event_id
if self.image_path is not None:
rv["image_path"] = self.image_path
if self.image_uuid is not None:
Expand All @@ -85,6 +88,8 @@ def __str__(self) -> str:
rv.append(" image-uuid=%s" % self.image_uuid)
if self.image_name is not None:
rv.append(" image-name=%s" % self.image_name)
if self.event_id is not None:
rv.append(" sentry-event-id=%s" % self.event_id)
return "".join(rv)


Expand Down
24 changes: 4 additions & 20 deletions src/sentry/lang/native/processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from sentry import options
from sentry.lang.native.error import SymbolicationFailed, write_error
from sentry.lang.native.symbolicator import FrameOrder, Symbolicator
from sentry.lang.native.symbolicator import FrameOrder, Symbolicator, handle_response_status
from sentry.lang.native.utils import (
get_event_attachment,
get_os_from_event,
Expand Down Expand Up @@ -198,22 +198,6 @@ def _merge_image(raw_image, complete_image, os, data):
_handle_image_status(status, raw_image, os, data)


def _handle_response_status(event_data, response_json):
if not response_json:
error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE)
elif response_json["status"] == "completed":
return True
elif response_json["status"] == "failed":
error = SymbolicationFailed(
message=response_json.get("message") or None, type=EventError.NATIVE_SYMBOLICATOR_FAILED
)
else:
logger.error("Unexpected symbolicator status: %s", response_json["status"])
error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE)

write_error(error, event_data)


def _merge_system_info(data, system_info):
set_path(data, "contexts", "os", "type", value="os") # Required by "get_os_from_event"

Expand Down Expand Up @@ -334,7 +318,7 @@ def process_minidump(symbolicator: Symbolicator, data: Any) -> Any:
metrics.incr("process.native.symbolicate.request")
response = symbolicator.process_minidump(data.get("platform"), minidump, rewrite_first_module)

if _handle_response_status(data, response):
if handle_response_status(data, response):
_merge_full_response(data, response)

# Emit Apple symbol stats
Expand All @@ -357,7 +341,7 @@ def process_applecrashreport(symbolicator: Symbolicator, data: Any) -> Any:
metrics.incr("process.native.symbolicate.request")
response = symbolicator.process_applecrashreport(data.get("platform"), report)

if _handle_response_status(data, response):
if handle_response_status(data, response):
_merge_full_response(data, response)

# Emit Apple symbol stats
Expand Down Expand Up @@ -480,7 +464,7 @@ def process_native_stacktraces(symbolicator: Symbolicator, data: Any) -> Any:
signal=signal,
)

if not _handle_response_status(data, response):
if not handle_response_status(data, response):
return data

# Emit Apple symbol stats
Expand Down
34 changes: 32 additions & 2 deletions src/sentry/lang/native/symbolicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@

from sentry import options
from sentry.attachments.base import CachedAttachment
from sentry.lang.native.error import SymbolicationFailed, write_error
from sentry.lang.native.sources import (
get_internal_artifact_lookup_source,
get_internal_source,
get_scraping_config,
sources_for_symbolication,
)
from sentry.lang.native.utils import Backoff
from sentry.models.eventerror import EventError
from sentry.models.project import Project
from sentry.net.http import Session
from sentry.objectstore import get_attachments_session
Expand Down Expand Up @@ -396,6 +398,30 @@ def process_jvm(
return self._process("symbolicate_jvm_stacktraces", "symbolicate-jvm", json=json)


def handle_response_status(event_data: Any, response_json: dict[str, Any]) -> bool | None:
"""Checks the response from Symbolicator and reports errors.
Returns `True` on success."""

if not response_json:
error = SymbolicationFailed(type=EventError.NATIVE_INTERNAL_FAILURE)
elif response_json["status"] == "completed":
return True
elif response_json["status"] == "failed":
error = SymbolicationFailed(
message=response_json.get("message") or None,
type=EventError.NATIVE_SYMBOLICATOR_FAILED,
event_id=response_json.get("event_id"),
)
else:
logger.error("Unexpected symbolicator status: %s", response_json["status"])
error = SymbolicationFailed(
type=EventError.NATIVE_INTERNAL_FAILURE, event_id=response_json.get("event_id")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this need to come from the response? I assume sentry can just send this id as a scope when making the request?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't quite understand what you mean. Are you suggesting that we store the event ID of the Sentry error on the scope?

Copy link
Copy Markdown
Member

@Dav1dde Dav1dde Nov 26, 2025

Choose a reason for hiding this comment

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

I don't understand why the event id needs to come from the Symbolicator response, Sentry should already have it.

One possibility is to instead setup the SDK scope in a way that the event id is always present, then there is also no need to parse it from the response. Assuming your use is for a Sentry error.

)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: Missing Sentry event capture for unexpected status

When Symbolicator returns an unexpected status value, the code logs an error but doesn't capture it to Sentry, so response_json.get("event_id") returns None. This prevents including a Sentry event ID for tracking internal errors, which contradicts the PR's goal. Unlike HTTP-level errors (lines 525-530), unexpected statuses don't get captured to Sentry, creating an inconsistency in error tracking.

Fix in Cursor Fix in Web


write_error(error, event_data)
return None


class TaskIdNotFound(Exception):
pass

Expand Down Expand Up @@ -496,9 +522,13 @@ def _request(self, method, path, **kwargs):
else:
with sentry_sdk.isolation_scope():
sentry_sdk.set_extra("symbolicator_response", response.text)
sentry_sdk.capture_message("Symbolicator request failed")
event_id = sentry_sdk.capture_message("Symbolicator request failed")

json = {"status": "failed", "message": "internal server error"}
json = {
"status": "failed",
"message": "internal server error",
"event_id": event_id,
}

return json
except (OSError, RequestException) as e:
Expand Down
Loading