{"record":{"id":"fce8832d0434808e","repo":"iflytek/astron-agent","slug":"s-must-contain-only-ascii-letters-digits-or","errorCode":null,"errorMessage":"%s must contain only ASCII letters, digits, '.', '_', '~', or '-'","messagePattern":"(.+?) must contain only ASCII letters, digits, '\\.', '_', '~', or '-'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tenant/config/bootstrap_credentials.go","lineNumber":146,"sourceCode":"\t\treturn \"\", errors.New(\"credential file cannot be read\")\n\t}\n\tif len(data) > maxCredentialFileBytes {\n\t\treturn \"\", errors.New(\"credential file is too large\")\n\t}\n\treturn strings.TrimSpace(string(data)), nil\n}\n\nfunc validateCredential(name, value string) error {\n\tlength := utf8.RuneCountInString(value)\n\tif !utf8.ValidString(value) || length < tenantCredentialMinLength || length > tenantCredentialMaxLength {\n\t\treturn fmt.Errorf(\"%s must contain 32-50 valid UTF-8 characters\", name)\n\t}\n\tfor _, character := range value {\n\t\tif unicode.IsControl(character) {\n\t\t\treturn fmt.Errorf(\"%s must not contain control characters\", name)\n\t\t}\n\t\tif !isSafeCredentialCharacter(character) {\n\t\t\treturn fmt.Errorf(\"%s must contain only ASCII letters, digits, '.', '_', '~', or '-'\", name)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc isSafeCredentialCharacter(character rune) bool {\n\treturn character >= 'a' && character <= 'z' ||\n\t\tcharacter >= 'A' && character <= 'Z' ||\n\t\tcharacter >= '0' && character <= '9' ||\n\t\tcharacter == '.' || character == '_' || character == '~' || character == '-'\n}\n","sourceCodeStart":128,"sourceCodeEnd":158,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/tenant/config/bootstrap_credentials.go#L128-L158","documentation":"validateCredential rejects a bootstrap credential (TENANT_KEY or TENANT_SECRET) that contains a character outside the safe set: ASCII letters, digits, '.', '_', '~', '-'. These credentials are placed into HTTP headers and persisted data, so the service enforces a strict character whitelist to guarantee header-safe, header-injection-free values. The value is checked after the 32-50 character length check in validateCredential (core/tenant/config/bootstrap_credentials.go:136).","triggerScenarios":"LoadTenantBootstrapCredentials reads TENANT_KEY/TENANT_SECRET from the environment or from a TENANT_KEY_FILE/TENANT_SECRET_FILE, then calls credentials.Validate() (or directly validateCredential via credentialFromEnvironmentOrFile); the error is returned when any rune in the value is not in the safe set — e.g. base64 '+' or '/', whitespace, newline at file end beyond trimming, quotes, or non-ASCII characters.","commonSituations":"Generating credentials with base64 (which emits '+' and '/'), copying secrets with trailing whitespace or smart quotes, pasting values containing '=' padding, editing credential files with an editor that inserts UTF-8 characters, or Helm/K8s Secret manifests adding newlines that survive TrimSpace in the middle of the value.","solutions":["Regenerate the credential using only the allowed alphabet (hex or base64url: replace '+' with '-', '/' with '_', strip '=' padding) and redeploy.","Inspect the value with `env | grep TENANT_` or `cat -A TENANT_KEY_FILE` to find offending characters (quotes, '+', '/', spaces, non-ASCII) and remove them.","If loading from a credential file, ensure the file contains exactly the credential with no extra characters beyond leading/trailing whitespace (which is trimmed) — recreate it with `printf '%s' <value> > file`.","If the credential must legally contain other characters (e.g. base64 standard alphabet), it is unsupported; encode/transform the value before deployment since the whitelist is fixed in isSafeCredentialCharacter."],"exampleFix":"// before (base64 with +, /, =)\nTENANT_KEY=\"aB3d+/EfGh==\"\n// after (base64url, no padding, safe alphabet)\nTENANT_KEY=\"aB3d-EfGhIjKlMnOpQrStUvWxYz0123456789ABCDEF\"","handlingStrategy":"validation","validationCode":"re := regexp.MustCompile(`^[A-Za-z0-9._~-]{32,50}$`)\nif !re.MatchString(os.Getenv(\"TENANT_KEY\")) || !re.MatchString(os.Getenv(\"TENANT_SECRET\")) {\n\treturn errors.New(\"TENANT_KEY/TENANT_SECRET must be 32-50 chars of [A-Za-z0-9._~-]\")\n}","typeGuard":"func isSafeCredential(value string) bool {\n\tif n := utf8.RuneCountInString(value); n < 32 || n > 50 || !utf8.ValidString(value) {\n\t\treturn false\n\t}\n\tfor _, r := range value {\n\t\tif !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '.' || r == '_' || r == '~' || r == '-') {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","tryCatchPattern":"creds, err := config.LoadTenantBootstrapCredentials()\nif err != nil {\n\tlog.Fatalf(\"bootstrap credentials invalid, check TENANT_KEY/TENANT_SECRET alphabet: %v\", err)\n}","preventionTips":["Generate credentials in base64url or hex so they only use the safe alphabet","Write credential files with printf '%s' to avoid editor-added characters","Run the same regex check in CI on deployment manifests","Never wrap credentials in quotes or spaces inside secret manifests"],"tags":["configuration","validation","credentials","go"],"backgroundTag":"invalid-identifier-format","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}