{"record":{"id":"7edd7e1c2b5c83ed","repo":"aeron-io/aeron","slug":"livechannel-must-be-set","errorCode":null,"errorMessage":"liveChannel must be set","messagePattern":"liveChannel must be set","errorType":"validation","errorClass":"ConfigurationException","httpStatus":null,"severity":"error","filePath":"aeron-archive/src/main/java/io/aeron/archive/client/PersistentSubscription.java","lineNumber":1692,"sourceCode":"        {\n            if ((boolean)IS_CONCLUDED_VH.getAndSet(this, true))\n            {\n                throw new ConcurrentConcludeException();\n            }\n\n            if (Aeron.NULL_VALUE == recordingId)\n            {\n                throw new ConfigurationException(\"recordingId must be set\");\n            }\n\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)","sourceCodeStart":1674,"sourceCodeEnd":1710,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-archive/src/main/java/io/aeron/archive/client/PersistentSubscription.java#L1674-L1710","documentation":"PersistentSubscription.Context.conclude() validates that a liveChannel URI was configured before the subscription can be created. The live channel is the Aeron channel on which the subscription originally listens for live data before switching to replay. A ConfigurationException is thrown when the channel is null or empty because a persistent subscription cannot be constructed without it.","triggerScenarios":"Calling PersistentSubscription.Context.conclude() (directly or via a constructor/build path) without ever calling context.liveChannel(String), or setting it to null/empty string. Also occurs when a builder for a persistent subscription omits the live channel.","commonSituations":"Constructing a PersistentSubscription programmatically with only recordingId/replay parameters set; copy-pasting a replay-only setup and forgetting the live side; migration from older archive client APIs where the live channel defaulted implicitly.","solutions":["Call liveChannel(\"aeron:udp?endpoint=...\") (or ipc URI) on the PersistentSubscription.Context before conclude().","If building via a helper/builder, pass the live channel in the constructor or setter it exposes.","Ensure the value is non-null and non-empty (Strings.isEmpty is the check)."],"exampleFix":"// before\nfinal PersistentSubscription.Context ctx = new PersistentSubscription.Context()\n    .recordingId(recordingId)\n    .replayChannel(\"aeron:udp?endpoint=localhost:10001\")\n    .replayStreamId(7);\nctx.conclude(); // throws: liveChannel must be set\n\n// after\nfinal PersistentSubscription.Context ctx = new PersistentSubscription.Context()\n    .recordingId(recordingId)\n    .liveChannel(\"aeron:udp?endpoint=localhost:10000\")\n    .liveStreamId(5)\n    .replayChannel(\"aeron:udp?endpoint=localhost:10001\")\n    .replayStreamId(7);\nctx.conclude();","handlingStrategy":"validation","validationCode":"if (Strings.isEmpty(ctx.liveChannel())) {\n    throw new IllegalArgumentException(\"liveChannel must be set before conclude()\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    ctx.conclude();\n} catch (ConfigurationException e) {\n    // log and re-check liveChannel configuration\n}","preventionTips":["Always set liveChannel, liveStreamId, replayChannel and replayStreamId together in one builder method.","Validate all channel URIs are non-empty right after loading config, before constructing the subscription."],"tags":["aeron-archive","configuration","missing-required-argument"],"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-15T23:17:13.987Z"}