Skip to content

Issue: Source Maps Not Correctly Mapping Stack Trace in Sentry #2127

Open

Description

CLI Version

2.33.1

Operating System and Architecture

  • macOS (arm64)
  • macOS (x86_64)
  • Linux (i686)
  • Linux (x86_64)
  • Linux (armv7)
  • Linux (aarch64)
  • Windows (i686)
  • Windows (x86_64)

Operating System Version

Amazon Linux 2/5.9.4

Link to reproduction repository

No response

CLI Command

sentry-cli sourcemaps upload

Exact Reproduction Steps

Steps to Reproduce

Initialize Sentry:

I initialized Sentry in my Node.js project on top of index file by import sentry-instrument.js.

import * as Sentry from '@sentry/node';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  integrations: [
    Sentry.httpIntegration({ tracing: true }),
    Sentry.rewriteFramesIntegration({
      iteratee: (frame) => {
        frame.filename = frame.filename.replace(/^\//, '');
        return frame;
      }
    })
    // enable Express.js middleware tracing
  ],

  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
  tracesSampleRate: 1.0,
  // Set sampling rate for profiling - this is relative to tracesSampleRate
  profilesSampleRate: 1.0
});

Configure CodeBuild:

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 18
    commands:
      - n 18.17.1
  pre_build:
    commands:
      - echo Starting npm install
      - npm install --legacy-peer-deps && npm install --only=dev --legacy-peer-deps
      - npm i -g sequelize-cli
      - npm install -g @sentry/cli
  build:
    commands:
      - echo Starting to run migrations
      - echo Starting npm build
      - npm run build --loglevel verbose
      - npm prune --production
      - sentry-cli sourcemaps inject --org numu-04 --project numu-node-api ./dist
  post_build:
    commands:
      - echo "Uploading source maps to Sentry"
      - sentry-cli sourcemaps upload --org numu-04 --project numu-node-api ./dist --url-prefix '~/var/app/current/src'
artifacts:
  files:
    - package.json
    - package-lock.json
    - dist/**/*
    - node_modules/**/*
    - .ebextensions/**/*
    - .platform/**/*
    - .npmrc

Push the Code:

I pushed the code to the repository, triggering the pipeline in AWS CodePipeline.
The pipeline runs, and the build specifications are executed in CodeBuild.

Deploy to Sentry and Elastic Beanstalk:
build code was injected with debug-id and uploaded to sentry.

I created an endpoint in my application that intentionally throws an exception to test Sentry's error reporting functionality. This is how I verified that the errors are being sent to Sentry.

  /** Express Error Handler */
  api.use(
    Sentry.expressErrorHandler({
      shouldHandleError(error) {
        if (error.status > 499) {
          Sentry.captureException(error);
          return true;
        }

        return false;
      }
    })
  );

Expected Results

I expect the Sentry stack trace to accurately map to the original source code, displaying the exact line of code where the error occurred, based on the uploaded source maps. This would help in pinpointing the issue directly in the original code rather than in the build artifacts.

Actual Results

When I throw an error to Sentry via API, Stack trace only shows line number of the error correctly. However, it does not display corresponding code from original source file. Source maps are not being utilized as expected to reveal specific line of code where error occurred.

Image

and this event's json has no debug-id (i am assuming here is the problem)

Image

Uploaded source maps on Sentry do include this file.

Image

I also checked uploaded code on the server, and it does have Sentry debug ID injected:

"use strict";
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="18a58218-****-****-****-9fc9262b7d65")}catch(e){}}();


var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _utils = require("../../utils");
var _base = require("../base/base.service");
var _country = require("../country");
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2["default"])(o), (0, _possibleConstructorReturn2["default"])(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2["default"])(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
var SentryService = /*#__PURE__*/function (_BaseService) {
  function SentryService() {
    (0, _classCallCheck2["default"])(this, SentryService);
    return _callSuper(this, SentryService, arguments);
  }
  (0, _inherits2["default"])(SentryService, _BaseService);
  return (0, _createClass2["default"])(SentryService, [{
    key: "fetchSentryCountry",
    value: // this service has error in it
    // so we can throw error on sentry
    // to test its functionality
    function () {
      var _fetchSentryCountry = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee() {
        return _regenerator["default"].wrap(function _callee$(_context) {
          while (1) switch (_context.prev = _context.next) {
            case 0:
              _context.prev = 0;
              _context.next = 3;
              return _country.CountryService.findByPk({
                id: 0
              }).then(function (data) {
                data.map(function (country) {
                  return country.name;
                });
              })["catch"](function (error) {
                throw new _utils.AppError({
                  error: error
                });
              });
            case 3:
              _context.next = 8;
              break;
            case 5:
              _context.prev = 5;
              _context.t0 = _context["catch"](0);
              throw new _utils.AppError({
                error: _context.t0
              });
            case 8:
            case "end":
              return _context.stop();
          }
        }, _callee, null, [[0, 5]]);
      }));
      function fetchSentryCountry() {
        return _fetchSentryCountry.apply(this, arguments);
      }
      return fetchSentryCountry;
    }()
  }]);
}(_base.BaseService);
var _default = exports["default"] = new SentryService();
//# sourceMappingURL=sentry.service.js.map
//# debugId=18a58218-****-****-****-9fc9262b7d65

Sentry source map code

"use strict";
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="18a58218-****-****-****-9fc9262b7d65")}catch(e){}}();


var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _utils = require("../../utils");
var _base = require("../base/base.service");
var _country = require("../country");
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2["default"])(o), (0, _possibleConstructorReturn2["default"])(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2["default"])(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
var SentryService = /*#__PURE__*/function (_BaseService) {
  function SentryService() {
    (0, _classCallCheck2["default"])(this, SentryService);
    return _callSuper(this, SentryService, arguments);
  }
  (0, _inherits2["default"])(SentryService, _BaseService);
  return (0, _createClass2["default"])(SentryService, [{
    key: "fetchSentryCountry",
    value: // this service has error in it
    // so we can throw error on sentry
    // to test its functionality
    function () {
      var _fetchSentryCountry = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee() {
        return _regenerator["default"].wrap(function _callee$(_context) {
          while (1) switch (_context.prev = _context.next) {
            case 0:
              _context.prev = 0;
              _context.next = 3;
              return _country.CountryService.findByPk({
                id: 0
              }).then(function (data) {
                data.map(function (country) {
                  return country.name;
                });
              })["catch"](function (error) {
                throw new _utils.AppError({
                  error: error
                });
              });
            case 3:
              _context.next = 8;
              break;
            case 5:
              _context.prev = 5;
              _context.t0 = _context["catch"](0);
              throw new _utils.AppError({
                error: _context.t0
              });
            case 8:
            case "end":
              return _context.stop();
          }
        }, _callee, null, [[0, 5]]);
      }));
      function fetchSentryCountry() {
        return _fetchSentryCountry.apply(this, arguments);
      }
      return fetchSentryCountry;
    }()
  }]);
}(_base.BaseService);
var _default = exports["default"] = new SentryService();
//# sourceMappingURL=sentry.service.js.map
//# debugId=18a58218-****-****-****-9fc9262b7d65

I initially tried using Sentry SDK version 7, but it did not work as expected. I then migrated to SDK version 8, yet I am encountering the same issue with stack trace mapping.

Logs

I'll share the full log output.

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

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions