{"record":{"id":"ad7786d24b92e46a","repo":"iflytek/astron-agent","slug":"s-must-not-contain-control-characters","errorCode":null,"errorMessage":"%s must not contain control characters","messagePattern":"(.+?) must not contain control characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tenant/config/bootstrap_credentials.go","lineNumber":143,"sourceCode":"\n\tdata, err := io.ReadAll(io.LimitReader(file, maxCredentialFileBytes+1))\n\tif err != nil {\n\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":125,"sourceCodeEnd":158,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/tenant/config/bootstrap_credentials.go#L125-L158","documentation":"After the length/UTF-8 check, validateCredential iterates the value and rejects any control character (unicode.IsControl), such as \\n, \\r, or \\t. The credential is placed in HTTP headers and storage, so embedded control characters are forbidden. The message names which variable (TENANT_KEY or TENANT_SECRET) contained them.","triggerScenarios":"A credential value read from a file or pasted into an env var contains a trailing/interior newline, carriage return (Windows CRLF), tab, or other control byte, and validateCredential reaches the control-character branch.","commonSituations":"Secret file created on Windows with CRLF line endings; echo instead of printf -n when writing the secret file; multi-line paste into a Kubernetes Secret; YAML block scalar (|) adding a trailing newline (though readCredentialFile trims edges, interior newlines still fail).","solutions":["Rewrite the secret file without trailing newline/control chars: printf '%s' \"$VALUE\" > file (use printf, not echo).","Convert CRLF to LF: dos2unix on the secret file, or regenerate it on Linux.","Ensure the secret is a single line with only ASCII letters, digits, and . _ ~ - characters.","If the value legitimately contained newlines, it is the wrong secret — retrieve the correct single-line credential."],"exampleFix":"// before\necho \"$TENANT_KEY\" > /run/secrets/tenant-key   # adds trailing \\n\n\n// after\nprintf '%s' \"$TENANT_KEY\" > /run/secrets/tenant-key","handlingStrategy":"validation","validationCode":"func hasControlChars(v string) bool {\n\tfor _, r := range v {\n\t\tif unicode.IsControl(r) { return true }\n\t}\n\treturn false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write secret files with printf '%s', never echo (which appends a newline).","Run dos2unix or generate secrets on Linux to avoid CRLF.","Use single-line string values (not block scalars) in YAML secrets."],"tags":["validation","credentials","encoding","go"],"backgroundTag":"invalid-argument-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"}