{"record":{"id":"e54d7b676501b856","repo":"testcontainers/testcontainers-java","slug":"invalid-protocol-protocol","errorCode":null,"errorMessage":"Invalid protocol: ${protocol}","messagePattern":"Invalid protocol: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java","lineNumber":45,"sourceCode":"            Object reusable = options.getValue(R2DBCDatabaseContainerProvider.REUSABLE_OPTION);\n            if (reusable instanceof String) {\n                builder.option(R2DBCDatabaseContainerProvider.REUSABLE_OPTION, Boolean.valueOf((String) reusable));\n            }\n            return builder.build();\n        }\n\n        private ConnectionFactoryOptions removeProxying(ConnectionFactoryOptions options) {\n            // To delegate to the next factory provider, inspect the PROTOCOL and convert it to the next DRIVER and PROTOCOL values.\n            //\n            // example:\n            //   | Property | Input           | Output       |\n            //   |----------|-----------------|--------------|\n            //   | DRIVER   | tc              | postgres     |\n            //   | PROTOCOL | postgres        | <empty>      |\n\n            String protocol = (String) options.getRequiredValue(ConnectionFactoryOptions.PROTOCOL);\n            if (protocol.trim().length() == 0) {\n                throw new IllegalArgumentException(\"Invalid protocol: \" + protocol);\n            }\n            String[] protocols = protocol.split(\":\", 2);\n            String driverDelegate = protocols[0];\n\n            // when protocol does NOT contain COLON, the length becomes 1\n            String protocolDelegate = protocols.length == 2 ? protocols[1] : \"\";\n\n            return ConnectionFactoryOptions\n                .builder()\n                .from(options)\n                .option(ConnectionFactoryOptions.DRIVER, driverDelegate)\n                .option(ConnectionFactoryOptions.PROTOCOL, protocolDelegate)\n                .build();\n        }\n\n        @Override\n        public boolean supports(ConnectionFactoryOptions options) {\n            return DRIVER.equals(options.getValue(ConnectionFactoryOptions.DRIVER));","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/testcontainers/testcontainers-java/blob/8e549514e3f01c57d70546fbb8599d138f3903e5/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java#L27-L63","documentation":"Hidden (the internal r2dbc Testcontainers ConnectionFactory provider) requires a PROTOCOL option of the form '<driverDelegate>:[<protocolDelegate>]' (e.g. 'tc:postgres'). An empty/whitespace-only protocol yields IllegalArgumentException 'Invalid protocol: <protocol>'.","triggerScenarios":"Creating a connection factory with ConnectionFactoryOptions.PROTOCOL set to \"\" or whitespace, or omitting the tc prefix portion so parsing yields nothing usable; called from ConnectionFactory creation flow (create → removeProxying).","commonSituations":"Building r2dbc URLs manually and forgetting the 'tc:' driver segment (e.g. r2dbc:tc:postgresql:///db vs a malformed protocol); copying options maps and clearing the protocol; dynamic config that returns an empty string.","solutions":["Set the protocol to '<driver>:<protocol>' form, e.g. ConnectionFactoryOptions.valueOf(\"tc:postgresql\") or URL r2dbc:tc:postgresql://localhost/d/testdb","Ensure the driver segment before the first ':' is non-empty (e.g. 'tc')","Check that no code path overwrites PROTOCOL with an empty value before create()"],"exampleFix":"// before\nConnectionFactoryOptions.builder().option(PROTOCOL, \"\")...\n// after\nConnectionFactoryOptions.builder()\n    .option(DRIVER, \"tc\")\n    .option(PROTOCOL, \"postgresql\")  // resolves to tc:postgresql\n    ...","handlingStrategy":"validation","validationCode":"String protocol = (String) options.getRequiredValue(ConnectionFactoryOptions.PROTOCOL);\nif (protocol == null || protocol.trim().isEmpty() || !protocol.contains(\":\")) {\n    throw new IllegalArgumentException(\"PROTOCOL must be '<driver>:<db>', e.g. tc:postgresql\");\n}","typeGuard":"boolean isValidTcProtocol(Object p) { return p instanceof String s && !s.trim().isEmpty() && s.contains(\":\") && !s.startsWith(\":\"); }","tryCatchPattern":"try {\n    connectionFactory.create();\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Invalid protocol\")) { /* rebuild options with tc:<db> */ }\n    throw e;\n}","preventionTips":["Always use r2dbc:tc:<database> URL format so the tc driver segment is present","Never blank out PROTOCOL when copying ConnectionFactoryOptions","Add a smoke connection test that exercises create() early in startup"],"tags":["r2dbc","testcontainers","url","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"8e549514e3f01c57d70546fbb8599d138f3903e5","analyzedAt":"2026-09-12T14:56:41.227Z","contentChangedAt":"2026-09-12T14:56:41.227Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}