{"record":{"id":"53036fc820a9a305","repo":"aeron-io/aeron","slug":"replaystreamid-must-be-set","errorCode":null,"errorMessage":"replayStreamId must be set","messagePattern":"replayStreamId must be set","errorType":"validation","errorClass":"ConfigurationException","httpStatus":null,"severity":"error","filePath":"aeron-archive/src/main/java/io/aeron/archive/client/PersistentSubscription.java","lineNumber":1702,"sourceCode":"\n            if (Aeron.NULL_VALUE == liveStreamId)\n            {\n                throw new ConfigurationException(\"liveStreamId must be set\");\n            }\n\n            if (Strings.isEmpty(liveChannel))\n            {\n                throw new ConfigurationException(\"liveChannel must be set\");\n            }\n\n            if (Strings.isEmpty(replayChannel))\n            {\n                throw new ConfigurationException(\"replayChannel must be set\");\n            }\n\n            if (Aeron.NULL_VALUE == replayStreamId)\n            {\n                throw new ConfigurationException(\"replayStreamId must be set\");\n            }\n\n            if (null == aeronArchiveContext)\n            {\n                throw new ConfigurationException(\"aeronArchiveContext must be set\");\n            }\n\n            if (null == listener)\n            {\n                listener = new NoOpPersistentSubscriptionListener();\n            }\n\n            if (0 > recordingId)\n            {\n                throw new ConfigurationException(\"invalid recordingId \" + recordingId);\n            }\n\n            if (FROM_LIVE > startPosition)","sourceCodeStart":1684,"sourceCodeEnd":1720,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-archive/src/main/java/io/aeron/archive/client/PersistentSubscription.java#L1684-L1720","documentation":"conclude() checks that replayStreamId was set (i.e. not Aeron.NULL_VALUE). The replay stream id identifies the stream on the replayChannel used to deliver recorded data from the archive. Leaving it at the NULL_VALUE sentinel means the replay destination is incompletely specified, so a ConfigurationException is thrown.","triggerScenarios":"conclude() called with replayStreamId never assigned (defaults to Aeron.NULL_VALUE), or explicitly set to Aeron.NULL_VALUE (-1).","commonSituations":"Setting liveStreamId and assuming it applies to replay too; forgetting the streamId setter while changing channels; programmatic construction where streamIds come from separate config keys and the replay one is missing.","solutions":["Call replayStreamId(<positive int>) on the Context before conclude().","If the same stream id is used for both paths, set it explicitly on both liveStreamId and replayStreamId.","Ensure the config source supplying the replay stream id is present and parsed."],"exampleFix":"// before\nctx.replayChannel(\"aeron:udp?endpoint=localhost:10001\");\nctx.conclude(); // throws: replayStreamId must be set (still NULL_VALUE)\n\n// after\nctx.replayChannel(\"aeron:udp?endpoint=localhost:10001\");\nctx.replayStreamId(7);\nctx.conclude();","handlingStrategy":"validation","validationCode":"if (Aeron.NULL_VALUE == ctx.replayStreamId()) {\n    throw new IllegalArgumentException(\"replayStreamId must be set before conclude()\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    ctx.conclude();\n} catch (ConfigurationException e) {\n    // fall back to liveStreamId if streams are shared\n}","preventionTips":["Never rely on defaults for stream ids; set both liveStreamId and replayStreamId explicitly.","Centralize stream id constants so live and replay ids are defined side by side."],"tags":["aeron-archive","configuration","stream-id"],"backgroundTag":"missing-required-config-field","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}