Hidden Assumptions in openreplay

13 assumptions this code never checks · 5 critical · spanning Ordering, Temporal, Resource, Scale, Environment, Domain, Contract, Shape

Every codebase relies on things it never checks. Most of them are routine. CodeSea looked at openreplay/openreplay and picked out the few most likely to cause trouble. The full list is just below.

Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here. The rest are minor; they're under "Show everything".

Worth your attention first

If Kafka partitioning fails or messages for a session arrive across multiple partitions, the session timeout logic will see events out of order, potentially ending sessions prematurely or extending them incorrectly, corrupting session duration metrics

Worth your attention first

Sessions for mobile apps with background behavior or slow networks get artificially terminated early, while bot traffic or automated tests create artificially long sessions, skewing analytics and potentially losing real user data

Worth your attention first

Asset caching grows unbounded over time, eventually exhausting disk space and causing the entire asset service to fail, breaking session replay functionality when screenshots and source maps can't be retrieved

Show everything (10 more)
Scale

The MessageSizeLimit configuration adequately handles the largest possible user interactions, but there's no validation that message producers respect this limit or that downstream consumers can handle messages approaching this size

If this fails: Large DOM mutations or massive JavaScript payloads exceed the limit, causing message rejection at Kafka ingestion but silent data loss since the application continues processing without the oversized events, resulting in incomplete session replays

backend/cmd/db/main.go:cfg.MessageSizeLimit
Environment

The process.env.maxHttpBufferSize environment variable, if present, contains a valid numeric value that can be parsed as a float and converted to bytes by multiplying by 1e6

If this fails: If maxHttpBufferSize contains non-numeric data, parseFloat returns NaN, causing WebSocket connections to use NaN * 1e6 as buffer size, which becomes 0 bytes, immediately dropping all real-time assistance data and breaking live support features

assist/utils/wsServer.js:createSocketIOServer
Domain

ClickHouse event records always contain the expected performance timing properties (dom_content_loaded_event_time, first_contentful_paint_time, etc.) as valid numeric strings that can be cast to Float32

If this fails: If browser events omit timing data or send non-numeric values (like 'undefined' or null), the CAST operations produce null values that NULLIF removes, causing performance alert thresholds to evaluate against incomplete data and miss critical performance regressions

api/chalicelib/core/alerts/alerts_processor.py:LeftToDb
Contract

All binary message data passed to the canvas image iterator contains valid message headers with correctly formatted message types and session IDs that can be deserialized by messages.ReadMessage()

If this fails: Malformed binary data from network corruption or version mismatches causes ReadMessage to panic, crashing the entire canvas service and preventing screenshot generation for all sessions until the service restarts

backend/cmd/canvases/main.go:messages.NewImagesMessageIterator
Temporal

Message timestamps in the stream represent the actual time when user events occurred in the browser, not when they were processed by the backend, allowing accurate session timeout calculations

If this fails: Network delays or message queue backlogs cause timestamps to represent processing time rather than event time, making session timeouts fire based on system lag rather than user inactivity, incorrectly ending active sessions during high load

backend/cmd/ender/main.go:sessionEndGenerator.UpdateSession
Resource

A 20-minute cleanup interval is sufficient to prevent memory leaks from accumulated asset cache entries, and the system can handle all cleanup operations within a single 20-minute window

If this fails: During high traffic periods with many unique URLs, memory usage grows faster than the 20-minute cleanup can process, eventually causing out-of-memory crashes in the assets service and losing cached data needed for session replay

backend/cmd/assets/main.go:tick = time.Tick(20 * time.Minute)
Shape

All Message structs deserialized from Kafka contain session IDs that exist as valid sessions in the sessions manager and can be looked up without error

If this fails: Race conditions where messages arrive after a session is deleted from Redis cause datasaver to fail session lookups, dropping events for recently ended sessions and creating gaps in session replay data near session boundaries

backend/internal/db/datasaver/datasaver.go:saver processing
Environment

The JWT secret key is provided through configuration and contains sufficient entropy to prevent brute force attacks, but there's no validation of key strength or rotation requirements

If this fails: Weak or default JWT secrets allow attackers to forge authentication tokens, gaining unauthorized access to session data and user recordings from any project in the system

backend/cmd/api/main.go:cfg.JWTSecret
Contract

The hardcoded msgFilter array contains all message types that the datasaver needs to process, and this list remains synchronized with message types generated by the tracker without any automated verification

If this fails: When new message types are added to the tracker, they won't be filtered into the database consumer, causing new event data to be silently ignored and creating incomplete session replays missing the newest interaction types

backend/cmd/db/main.go:queue.NewConsumer msgFilter
Scale

The number of concurrent WebSocket connections per room stays within Socket.IO's default memory limits, and room-based operations complete within reasonable time bounds

If this fails: Large assistance sessions with many concurrent agents cause fetchSockets() operations to timeout or consume excessive memory, degrading performance for all assistance sessions and potentially causing the entire assist service to become unresponsive

assist/utils/wsServer.js:io.in(roomID).fetchSockets()

See the full structural analysis of openreplay: the pipeline, data models, and system behavior that put these assumptions in context.

Full analysis of openreplay/openreplay →

Frequently Asked Questions

What does openreplay assume that could break in production?

The one most likely to cause trouble: Kafka partitions for each session maintain message ordering and all messages for a session flow through the same partition, allowing sessionEndGenerator.UpdateSession() to process events in chronological order If this fails, If Kafka partitioning fails or messages for a session arrive across multiple partitions, the session timeout logic will see events out of order, potentially ending sessions prematurely or extending them incorrectly, corrupting session duration metrics

How many hidden assumptions does openreplay have?

CodeSea found 13 assumptions openreplay relies on but never validates, 5 of them critical, spanning Ordering, Temporal, Resource, Scale, Environment, Domain, Contract, Shape. Most are routine — the analysis flags the two or three most likely to actually bite.

What is a hidden assumption?

Something the code depends on but never checks: a data shape, an ordering, an environment condition, a scale limit, or a contract with another service. It holds until the world it runs in changes, then fails silently.