{"record":{"id":"9066e678c68dab5d","repo":"OtterMind/Chat2DB","slug":"invalid-generic-what-value","errorCode":null,"errorMessage":"Invalid generic {what}: {value}","messagePattern":"Invalid generic (.+?): (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"chat2db-community-server/chat2db-community-plugins/chat2db-community-generic/src/main/java/ai/chat2db/plugin/generic/GenericSqlGuards.java","lineNumber":30,"sourceCode":" * The generic adapter serves mixed dialects via DBConfig templates (e.g. DuckDB wraps\n * placeholders in single quotes, TDengine uses bare identifier positions), so treatment\n * is chosen per placeholder by inspecting the template; no single dialect quote char is\n * hard-coded. Escaping itself lives in {@link GenericIdentifierProcessor}.\n */\npublic final class GenericSqlGuards {\n\n    private static final Pattern SAFE_IDENTIFIER_PATTERN = Pattern.compile(\"^[A-Za-z0-9_$]+$\");\n\n    private GenericSqlGuards() {\n    }\n\n    /**\n     * Validate a strict identifier token for bare-identifier template positions, where the\n     * generic adapter cannot know the dialect's identifier quote char.\n     */\n    public static String requireSafeIdentifier(String value, String what) {\n        if (value == null || !SAFE_IDENTIFIER_PATTERN.matcher(value).matches()) {\n            throw new IllegalArgumentException(\"Invalid generic \" + what + \": \" + value);\n        }\n        return value;\n    }\n\n    /**\n     * Sanitize a value that DBConfig substitutes for {@code placeholder} in the given\n     * generic.json SQL template. A placeholder wrapped in single quotes ('{database}')\n     * lands in string-literal position and gets literal escaping; a bare placeholder\n     * ({database}) lands in identifier position and must pass the identifier whitelist.\n     */\n    public static String sanitizeTemplateValue(String template, String placeholder, String value) {\n        if (template == null || StringUtils.isBlank(value)) {\n            return value;\n        }\n        if (template.contains(\"'\" + placeholder + \"'\")) {\n            return GenericIdentifierProcessor.INSTANCE.escapeString(value);\n        }\n        return requireSafeIdentifier(value, placeholder);","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-plugins/chat2db-community-generic/src/main/java/ai/chat2db/plugin/generic/GenericSqlGuards.java#L12-L48","documentation":"Thrown by GenericSqlGuards.requireSafeIdentifier when a value fails the strict identifier allow-list ^[A-Za-z0-9_$]+$. The generic adapter cannot know each dialect's identifier quote character, so bare-identifier template positions are restricted to this safe character set rather than escaped. Null or any character outside [A-Za-z0-9_$] is rejected.","triggerScenarios":"Calling GenericSqlGuards.requireSafeIdentifier(value, what) with a null value or a value containing spaces, quotes, dots, unicode, or punctuation; or DBConfig substituting a value for a bare placeholder like {database} via sanitizeTemplateValue.","commonSituations":"A schema/database/table name containing a hyphen, space, or dot used in a generic SQL template that has no quoting; a non-ASCII identifier from a migrated schema; an empty or null identifier passed to a generic connection/list command.","solutions":["Use a simple identifier (letters, digits, underscore, dollar) for values that flow into bare-identifier generic templates.","If the name needs special characters, use a dialect-specific path that quotes identifiers instead of the generic template.","Validate names against SAFE_IDENTIFIER_PATTERN before submitting a generic SQL request."],"exampleFix":"// before\nString db = \"my-db\"; // hyphen not in safe set\nGenericSqlGuards.requireSafeIdentifier(db, \"database\");\n\n// after\nString db = \"my_db\"; // only [A-Za-z0-9_$]\nGenericSqlGuards.requireSafeIdentifier(db, \"database\");","handlingStrategy":"validation","validationCode":"private static final java.util.regex.Pattern SAFE_ID = java.util.regex.Pattern.compile(\"^[A-Za-z0-9_$]+$\");\nstatic boolean isSafeGenericIdentifier(String v) {\n    return v != null && SAFE_ID.matcher(v).matches();\n}","typeGuard":"static String safeIdOrNull(String v) {\n    return (v != null && SAFE_ID.matcher(v).matches()) ? v : null;\n}","tryCatchPattern":null,"preventionTips":["Keep names for generic templates within [A-Za-z0-9_$].","Use dialect-specific quoted-identifier paths for names with special characters.","Validate identifiers client-side before submitting generic SQL requests."],"tags":["generic","sql-guard","identifier","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}