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

  1. Regenerate the credential using only the allowed alphabet (hex or base64url: replace '+' with '-', '/' with '_', strip '=' padding) and redeploy.
  2. Inspect the value with `env | grep TENANT_` or `cat -A TENANT_KEY_FILE` to find offending characters (quotes, '+', '/', spaces, non-ASCII) and remove them.
  3. 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`.
  4. 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

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


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)