Redux Sentry Middleware
raw JSON → 0.2.2 verified Thu Apr 23 auth: no javascript abandoned
This package provides a Redux middleware designed to integrate Redux state and actions with Sentry's unified APIs (`@sentry/browser` and `@sentry/node`). It captures dispatched actions as Sentry breadcrumbs and attaches the last action and current Redux state as additional context to error reports. The package is a rewrite of `raven-for-redux` to support newer Sentry SDKs. The latest published version is 0.2.2, released on September 7, 2018, indicating it is no longer actively maintained and has been superseded by Sentry's official Redux integration, `Sentry.createReduxEnhancer`.
Common errors
error TypeError: Sentry.captureException is not a function ↓
cause The Sentry object passed to `createSentryMiddleware` is not properly initialized or is an incompatible version. This middleware expects a Sentry object with specific methods like `captureException`, `addBreadcrumb`, etc., typically from `@sentry/browser` or `@sentry/node`.
fix
Ensure
Sentry.init() has been called successfully before creating the middleware and that you are using a version of @sentry/browser or @sentry/node compatible with this middleware (likely older versions, pre-v7). error Error: Middleware is not a function ↓
cause This error can occur if `createSentryMiddleware` is imported incorrectly (e.g., as a named import when it's a default export) or if the arguments passed to it are incorrect or missing, causing it to return an invalid middleware function.
fix
Confirm that
createSentryMiddleware is imported as a default export (import createSentryMiddleware from 'redux-sentry-middleware';) and that it is called with the Sentry object as the first argument: createSentryMiddleware(Sentry, options?). error Sentry events are missing Redux state or action context, or data is truncated. ↓
cause The `breadcrumbDataFromAction`, `actionTransformer`, or `stateTransformer` options might be misconfigured, filtering out too much data, or returning data that exceeds Sentry's payload limits or type constraints (e.g., non-flat data for breadcrumbs).
fix
Review your
actionTransformer and stateTransformer to ensure they return relevant, serializable data within Sentry's size limits. For breadcrumbDataFromAction, verify that the returned object is 'flat' (only string values). Consider increasing normalizeDepth in your Sentry.init call if your state is very deep and important for debugging. Warnings
breaking This package is considered abandoned and has not been updated since September 2018. It is built for older Sentry SDKs (`@sentry/browser` and `@sentry/node` versions from that era). Using it with modern Sentry SDKs (v7+) is likely to cause compatibility issues or miss out on new Sentry features. Sentry now provides an official `Sentry.createReduxEnhancer` for Redux integration. ↓
fix Migrate to Sentry's official Redux integration, `Sentry.createReduxEnhancer`, which is actively maintained and designed for current Sentry SDK versions.
gotcha The `breadcrumbDataFromAction` option expects the returned `data` object to be 'flat' (values must be strings, not arrays or objects). Sending complex or deeply nested data here can lead to Sentry silently dropping the data or the entire event due to size limits. ↓
fix Carefully transform your action data within `breadcrumbDataFromAction` to ensure it is a flat object with string values. Only include essential, non-sensitive information.
gotcha Both `actionTransformer` and `stateTransformer` should return serializable values. Attempting to send excessively large actions or state objects can exceed Sentry's payload limits, causing events to be dropped. Furthermore, ensure you do not mutate the original `action` or `state` objects within these transformer functions, as this can lead to unpredictable behavior in your Redux store. ↓
fix Implement these transformers to filter or truncate large or sensitive data, returning a new, modified object rather than mutating the original. Increase Sentry's `normalizeDepth` during initialization if your state is genuinely deep and needs to be captured.
gotcha Order of middleware matters. `redux-sentry-middleware` should typically be placed after other middlewares that might intercept or emit actions (e.g., `redux-thunk`, `redux-promise`) to ensure it captures the final action being dispatched. However, it should be before any custom error-handling middleware if you intend Sentry to be the primary error reporter. ↓
fix When applying middleware using `applyMiddleware`, ensure `redux-sentry-middleware` is positioned appropriately in the array based on your desired error reporting and action flow. The Sentry blog suggests it should be the first argument to `applyMiddleware` if you want it to capture errors from other middleware. However, the README for this specific package suggests preceding it with intercepting middlewares. This might imply different behavior than newer official Sentry enhancers. For modern Sentry integrations, `Sentry.createReduxEnhancer` is typically passed directly to `createStore` or `configureStore` as an enhancer, not via `applyMiddleware`.
Install
npm install redux-sentry-middleware yarn add redux-sentry-middleware pnpm add redux-sentry-middleware Imports
- createSentryMiddleware wrong
import { createSentryMiddleware } from 'redux-sentry-middleware';correctimport createSentryMiddleware from 'redux-sentry-middleware'; - Sentry
import * as Sentry from '@sentry/browser'; // or import * as Sentry from '@sentry/node'; - applyMiddleware wrong
const applyMiddleware = require('redux').applyMiddleware;correctimport { applyMiddleware } from 'redux';
Quickstart
import * as Sentry from '@sentry/browser';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import createSentryMiddleware from 'redux-sentry-middleware';
// Initialize Sentry SDK
Sentry.init({
dsn: process.env.SENTRY_DSN ?? 'https://examplePublicKey@o0.ingest.sentry.io/0',
tracesSampleRate: 1.0,
// It's recommended to increase normalizeDepth for Redux states in Sentry
normalizeDepth: 10 // Or however deep your state context needs to be
});
// Example Reducer
const initialState = { count: 0, user: { name: 'Guest', id: null } };
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
// Simulate an error for testing
if (action.payload === 'error') {
throw new Error('Intentional Redux error on DECREMENT');
}
return { ...state, count: state.count - 1 };
case 'SET_USER':
return { ...state, user: action.payload };
default:
return state;
}
}
const rootReducer = combineReducers({ counter: counterReducer });
// Create Sentry Middleware instance
const sentryMiddleware = createSentryMiddleware(Sentry, {
breadcrumbDataFromAction: action => {
// Log specific action data, ensuring it's flat and not too large
if (action.type === 'SET_USER') {
return { userId: action.payload.id, userName: action.payload.name };
}
return undefined;
},
actionTransformer: action => {
// Remove sensitive data from actions before sending to Sentry
if (action.type === 'SET_USER' && action.payload && action.payload.password) {
const { password, ...safePayload } = action.payload;
return { ...action, payload: safePayload };
}
return action;
},
stateTransformer: state => {
// Remove sensitive data from state before sending to Sentry
if (state.counter && state.counter.user && state.counter.user.sensitiveInfo) {
const { sensitiveInfo, ...safeUser } = state.counter.user;
return { ...state, counter: { ...state.counter, user: safeUser } };
}
return state;
}
});
export const store = createStore(
rootReducer,
applyMiddleware(sentryMiddleware)
);
// Example usage
store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'SET_USER', payload: { id: 123, name: 'Alice', password: 'secret' } });
try {
store.dispatch({ type: 'DECREMENT', payload: 'error' });
} catch (e) {
console.error('Caught expected error from Redux dispatch:', e.message);
}
console.log('Final state:', store.getState());