{"record":{"id":"bd37ed0d7d06a490","repo":"alibaba/druid","slug":"minidle-must-0","errorCode":null,"errorMessage":"minIdle must >= 0","messagePattern":"minIdle must >= 0","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"warning","filePath":"core/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java","lineNumber":1129,"sourceCode":"    public void setNotFullTimeoutRetryCount(int notFullTimeoutRetryCount) {\n        this.notFullTimeoutRetryCount = notFullTimeoutRetryCount;\n    }\n\n    public int getMinIdle() {\n        return minIdle;\n    }\n\n    public void setMinIdle(int value) {\n        if (value == this.minIdle) {\n            return;\n        }\n\n        if (inited && value > this.maxActive) {\n            throw new IllegalArgumentException(\"minIdle greater than maxActive, \" + maxActive + \" must >= \" + this.minIdle);\n        }\n\n        if (minIdle < 0) {\n            throw new IllegalArgumentException(\"minIdle must >= 0\");\n        }\n\n        this.minIdle = value;\n    }\n\n    public int getMaxIdle() {\n        return maxIdle;\n    }\n\n    @Deprecated\n    public void setMaxIdle(int maxIdle) {\n        LOG.error(\"maxIdle is deprecated\");\n        this.maxIdle = maxIdle;\n    }\n\n    public int getInitialSize() {\n        return initialSize;\n    }","sourceCodeStart":1111,"sourceCodeEnd":1147,"githubUrl":"https://github.com/alibaba/druid/blob/fa8dc9912637a2f729eef9f55356621fec18d40e/core/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java#L1111-L1147","documentation":"setMinIdle contains a guard intended to reject negative values, but it checks the FIELD 'minIdle' (the old value) instead of the parameter 'value'. Because the field defaults to 0 and is only ever assigned through this setter, the check is effectively inert for detecting a newly-supplied negative value. Treat this message as evidence of an already-negative field (e.g. set via reflection or an inconsistent state) rather than a reliable guard against bad input.","triggerScenarios":"The branch fires only when this.minIdle (the existing field) is already < 0 at the time of a setMinIdle call - which normally cannot happen through the setter itself.","commonSituations":"Reflection/unsafe mutation left the minIdle field negative; a deserialized/reconstructed datasource object with an inconsistent field; essentially a defensive check that does not fire for the input you would expect.","solutions":["Do not rely on this setter to validate negative input - pre-validate the value yourself.","If you observe this error, find where the minIdle field was set negative (reflection, deserialization) and correct it.","Validate minIdle >= 0 before calling setMinIdle."],"exampleFix":"// before\n// ds.setMinIdle(-5); // NOT caught by this (buggy) guard\n\n// after - validate yourself\n// if (value < 0) throw new IllegalArgumentException(\"minIdle must >= 0\");\n// ds.setMinIdle(value);","handlingStrategy":"validation","validationCode":"int v = desiredMinIdle;\nif (v < 0) throw new IllegalArgumentException(\"minIdle must >= 0\");\n// self-validate, because the setter's guard checks the OLD field, not the new value\nds.setMinIdle(v);","typeGuard":null,"tryCatchPattern":"try {\n    ds.setMinIdle(value);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"minIdle must >= 0\")) {\n        // the field was already negative (reflection/deserialization); reconstruct the datasource cleanly\n    }\n    throw e;\n}","preventionTips":["Always pre-validate minIdle >= 0 yourself - the setter's internal check is buggy (checks old field).","Avoid reflection/deserialization that could leave the minIdle field negative.","Treat this error as a symptom of inconsistent object state, not bad input."],"tags":["datasource","pool-sizing","validation","bug"],"backgroundTag":null,"analyzedSha":"fa8dc9912637a2f729eef9f55356621fec18d40e","analyzedAt":"2026-08-14T04:55:06.789Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}