iflytek/astron-agent · error
must contain only ASCII letters, digits, '.', '_', '~', or
Error message
%s must contain only ASCII letters, digits, '.', '_', '~', or '-'
What it means
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).
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.
Example fix
// before (base64 with +, /, =) TENANT_KEY="aB3d+/EfGh==" // after (base64url, no padding, safe alphabet) TENANT_KEY="aB3d-EfGhIjKlMnOpQrStUvWxYz0123456789ABCDEF"
Defensive patterns
Strategy: validation
Validate before calling
re := regexp.MustCompile(`^[A-Za-z0-9._~-]{32,50}$`)
if !re.MatchString(os.Getenv("TENANT_KEY")) || !re.MatchString(os.Getenv("TENANT_SECRET")) {
return errors.New("TENANT_KEY/TENANT_SECRET must be 32-50 chars of [A-Za-z0-9._~-]")
} Type guard
func isSafeCredential(value string) bool {
if n := utf8.RuneCountInString(value); n < 32 || n > 50 || !utf8.ValidString(value) {
return false
}
for _, r := range value {
if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '.' || r == '_' || r == '~' || r == '-') {
return false
}
}
return true
} Try / catch
creds, err := config.LoadTenantBootstrapCredentials()
if err != nil {
log.Fatalf("bootstrap credentials invalid, check TENANT_KEY/TENANT_SECRET alphabet: %v", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- TENANT_ID must remain
- must contain 32-50 valid UTF-8 characters
- must not contain control characters
- database password is required
- size limit is invalid
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/fce8832d0434808e.
Report an issue: GitHub.
Appendix: source
Thrown at core/tenant/config/bootstrap_credentials.go:146
return "", errors.New("credential file cannot be read")
}
if len(data) > maxCredentialFileBytes {
return "", errors.New("credential file is too large")
}
return strings.TrimSpace(string(data)), nil
}
func validateCredential(name, value string) error {
length := utf8.RuneCountInString(value)
if !utf8.ValidString(value) || length < tenantCredentialMinLength || length > tenantCredentialMaxLength {
return fmt.Errorf("%s must contain 32-50 valid UTF-8 characters", name)
}
for _, character := range value {
if unicode.IsControl(character) {
return fmt.Errorf("%s must not contain control characters", name)
}
if !isSafeCredentialCharacter(character) {
return fmt.Errorf("%s must contain only ASCII letters, digits, '.', '_', '~', or '-'", name)
}
}
return nil
}
func isSafeCredentialCharacter(character rune) bool {
return character >= 'a' && character <= 'z' ||
character >= 'A' && character <= 'Z' ||
character >= '0' && character <= '9' ||
character == '.' || character == '_' || character == '~' || character == '-'
}
View on GitHub (pinned to 5e758547a8)