{"record":{"id":"996bec768fd15ab3","repo":"aeron-io/aeron","slug":"startupcanvasstimeoutns-value-must-be-a-multiple-of","errorCode":null,"errorMessage":"startupCanvassTimeoutNs=<value> must be a multiple of leaderHeartbeatTimeoutNs=<value>","messagePattern":"startupCanvassTimeoutNs=<value> must be a multiple of leaderHeartbeatTimeoutNs=<value>","errorType":"validation","errorClass":"ClusterException","httpStatus":null,"severity":"error","filePath":"aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModule.java","lineNumber":1785,"sourceCode":"                throw new UncheckedIOException(ex);\n            }\n\n            if (null == clusterMembers)\n            {\n                throw new ClusterException(\"ConsensusModule.Context.clusterMembers must be set\");\n            }\n\n            if (deleteDirOnStart)\n            {\n                IoUtil.delete(clusterDir, false);\n            }\n\n            IoUtil.ensureDirectoryExists(clusterDir, \"cluster\");\n            IoUtil.ensureDirectoryExists(markFileDir, \"mark file\");\n\n            if (startupCanvassTimeoutNs / leaderHeartbeatTimeoutNs < 2)\n            {\n                throw new ClusterException(\n                    \"startupCanvassTimeoutNs=\" + startupCanvassTimeoutNs +\n                    \" must be a multiple of leaderHeartbeatTimeoutNs=\" + leaderHeartbeatTimeoutNs);\n            }\n\n            if (null == clusterClock)\n            {\n                final String clockClassName = System.getProperty(\n                    CLUSTER_CLOCK_PROP_NAME, MillisecondClusterClock.class.getName());\n                try\n                {\n                    clusterClock = (ClusterClock)Class.forName(clockClassName).getConstructor().newInstance();\n                }\n                catch (final Exception e)\n                {\n                    throw new ClusterException(\"failed to instantiate ClusterClock \" + clockClassName, e);\n                }\n            }\n","sourceCodeStart":1767,"sourceCodeEnd":1803,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModule.java#L1767-L1803","documentation":"conclude() validates that startupCanvassTimeoutNs is at least twice leaderHeartbeatTimeoutNs (the check startupCanvassTimeoutNs / leaderHeartbeatTimeoutNs < 2). The canvass window must span multiple heartbeat intervals so election messaging can complete; a too-short canvass timeout makes leader election unreliable and is rejected.","triggerScenarios":"Setting Context.startupCanvassTimeoutNs() to a value less than 2x leaderHeartbeatTimeoutNs() before conclude().","commonSituations":"Tuning election timeouts for fast failover and shrinking canvass below the heartbeat multiple; copying partial config from another cluster; typo putting nanosecond vs millisecond values in the wrong field.","solutions":["Increase startupCanvassTimeoutNs to at least 2x (typically 5s vs 10s heartbeat: e.g. canvass 10s, heartbeat 5s)","Or decrease leaderHeartbeatTimeoutNs so the ratio is >= 2","Verify units — both values are nanoseconds"],"exampleFix":"// before\nctx.leaderHeartbeatTimeoutNs(5_000_000_000L)\n   .startupCanvassTimeoutNs(4_000_000_000L); // < 2x\n// after\nctx.leaderHeartbeatTimeoutNs(5_000_000_000L)\n   .startupCanvassTimeoutNs(10_000_000_000L); // 2x heartbeat","handlingStrategy":"validation","validationCode":"if (startupCanvassTimeoutNs / leaderHeartbeatTimeoutNs < 2) {\n    throw new IllegalArgumentException(\"startupCanvassTimeoutNs must be >= 2x leaderHeartbeatTimeoutNs\");\n}","typeGuard":null,"tryCatchPattern":"try { ctx.conclude(); } catch (ClusterException e) { if (e.getMessage().contains(\"startupCanvassTimeoutNs\")) { /* fix timeout ratio in config */ } }","preventionTips":["Keep defaults unless you understand election timing","Assert the 2x ratio in config validation code","Use consistent nanosecond units for both settings"],"tags":["configuration","timeout","election"],"backgroundTag":"invalid-config-value","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"}