{"record":{"id":"358035094e7be2ff","repo":"Billionmail/BillionMail","slug":"fail2ban-value-can-only-be-y-n-or-1-0","errorCode":null,"errorMessage":"fail2ban value can only be y/n or 1/0","messagePattern":"fail2ban value can only be y/n or 1/0","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/settings/settings.go","lineNumber":234,"sourceCode":"\t\t\treturn fmt.Errorf(\"port must be between 1-65535\")\n\t\t}\n\n\tcase \"IPV4_NETWORK\", \"ipv4_network\":\n\t\t// IPv4 network: CIDR format\n\t\tif !public.IsValidCIDR(value) {\n\t\t\treturn fmt.Errorf(\"IPv4 network format is incorrect, please use CIDR format (e.g. 192.168.1.0/24)\")\n\t\t}\n\n\tcase \"TZ\", \"timezone\":\n\t\t// Timezone: check if it is a valid timezone\n\t\tif !public.IsValidTimezone(value) {\n\t\t\treturn fmt.Errorf(\"invalid timezone\")\n\t\t}\n\n\tcase \"FAIL2BAN_INIT\", \"fail2ban\":\n\t\t// fail2ban: only allowed y/n or 1/0\n\t\tif value != \"y\" && value != \"n\" && value != \"1\" && value != \"0\" {\n\t\t\treturn fmt.Errorf(\"fail2ban value can only be y/n or 1/0\")\n\t\t}\n\tcase \"RETENTION_DAYS\", \"retention_days\":\n\t\t// retention_days: must be a number\n\t\tif _, err := strconv.Atoi(value); err != nil {\n\t\t\treturn fmt.Errorf(\"retention_days must be a number\")\n\t\t}\n\t}\n\n\t// General character check: not allowed dangerous characters\n\tif public.ContainsDangerousChars(value) {\n\t\treturn fmt.Errorf(\"configuration value contains illegal characters\")\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":216,"sourceCodeEnd":250,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/settings/settings.go#L216-L250","documentation":"The FAIL2BAN_INIT/fail2ban key is a boolean-style toggle and validateConfigValue only accepts the literal strings y, n, 1 or 0. Any other value (true/false, yes/no, on/off) is rejected because the value is written directly into fail2ban/service configuration that expects this restricted alphabet.","triggerScenarios":"Calling SetSystemConfig/SetSystemConfigKey with FAIL2BAN_INIT set to 'true', 'false', 'yes', 'no', 'enabled', 'Y'/'N' if the check is case-sensitive, or an empty string.","commonSituations":"Frontends or scripts send JSON booleans serialized as 'true'/'false', or users type 'yes'/'no' out of habit; case-sensitivity trips values like 'Yes'.","solutions":["Send exactly one of: 'y', 'n', '1' or '0'","Map booleans before the call: true→'1', false→'0'","Normalize case to lowercase ('Y'→'y') if your source data is uppercase","Verify the stored value after saving by reading the config back"],"exampleFix":"// before\nFAIL2BAN_INIT = true\n// after\nFAIL2BAN_INIT = 1","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['y','n','1','0']);\nif (!ALLOWED.has(flag)) throw new Error('fail2ban value can only be y/n or 1/0');","typeGuard":"function isFail2banFlag(v: string): v is 'y'|'n'|'1'|'0' {\n  return v === 'y' || v === 'n' || v === '1' || v === '0';\n}","tryCatchPattern":"try {\n  await api.setSystemConfigKey('FAIL2BAN_INIT', flag);\n} catch (e) {\n  if (String(e.message).includes('fail2ban')) {\n    notify('Use exactly y, n, 1 or 0 (lowercase)');\n  } else throw e;\n}","preventionTips":["Map booleans: true→'1', false→'0' before the API call","Lowercase the value before sending","Use a select/toggle control, not free text","Reject 'true'/'false'/'yes'/'no' at the form layer"],"tags":["validation","config","boolean","fail2ban"],"backgroundTag":"invalid-enum-value","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}