iflytek/astron-agent · error
credential file must be a regular non-symbolic-link file
Error message
credential file must be a regular non-symbolic-link file
What it means
The unix build of openCredentialFileNoFollow opens the credential file with O_NOFOLLOW; when the kernel returns ELOOP the path is a symbolic link and the function rejects it. Like the portable variant, this prevents symlink attacks on credential paths. A real regular file must be present at the path.
Solutions
- Pass the fully resolved real path to the regular file (resolve the symlink outside the library, e.g. filepath.EvalSymlinks before calling, then verify the result is regular)
- Configure the credential path to the final regular file rather than a symlinked indirection (e.g. mount secrets with subPath in Kubernetes)
- Replace the symlink with a real copy of the secret file
Example fix
// before
cfg.CredentialFile = "/run/secrets/tenant.key" // symlink created by secret store
// after
real, err := filepath.EvalSymlinks("/run/secrets/tenant.key")
cfg.CredentialFile = real // resolved regular file Defensive patterns
Strategy: validation
Validate before calling
real, err := filepath.EvalSymlinks(path)
if err != nil {
return err
}
info, err := os.Lstat(real)
if err != nil || !info.Mode().IsRegular() {
return fmt.Errorf("credential path %q must resolve to a regular file", path)
} Type guard
func isRealRegularFile(path string) bool {
real, err := filepath.EvalSymlinks(path)
if err != nil {
return false
}
info, err := os.Lstat(real)
return err == nil && info.Mode().IsRegular()
} Try / catch
f, err := openCredentialFileNoFollow(path)
if err != nil {
if err.Error() == "credential file must be a regular non-symbolic-link file" {
resolved, rerr := filepath.EvalSymlinks(path)
if rerr == nil {
f, err = openCredentialFileNoFollow(resolved)
}
}
if err != nil {
return err
}
} Prevention
- Configure the fully resolved secret path, not a symlinked indirection
- In Kubernetes, use subPath secret mounts so files are regular
- Check /run/secrets-style paths: many secret stores mount symlinks — resolve first
- Document the no-symlink requirement wherever credential paths are configured
When it happens
Trigger: Calling openCredentialFileNoFollow on Linux/Unix with fileName being a symlink (unix.Open returns ELOOP due to O_NOFOLLOW).
Common situations: Kubernetes-mounted secrets are symlinked files inside ..data directories — passing the symlinked leaf path directly triggers this; provisioning scripts creating symlinks to secrets; Docker secrets mounted via symlinks (e.g. /run/secrets/... indirection).
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- credential file must be a regular non-symbolic-link file
- credential file must be a regular non-symbolic-link file
- credential file is unavailable
- Skill resource URL is not allowed
- exceeds size limit
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/17023bbfde0dcf26.
Report an issue: GitHub.
Appendix: source
Thrown at core/tenant/config/credential_file_unix.go:24
"errors"
"os"
"golang.org/x/sys/unix"
)
// openCredentialFileNoFollow resolves and opens the credential in one kernel
// operation. O_NOFOLLOW prevents a path swap to a symbolic link between a
// separate path inspection and open; O_NONBLOCK prevents a hostile FIFO from
// blocking startup before the descriptor type is checked with fstat.
func openCredentialFileNoFollow(fileName string) (*os.File, error) {
fd, err := unix.Open(
fileName,
unix.O_RDONLY|unix.O_CLOEXEC|unix.O_NOFOLLOW|unix.O_NONBLOCK,
0,
)
if err != nil {
if errors.Is(err, unix.ELOOP) {
return nil, errors.New(
"credential file must be a regular non-symbolic-link file",
)
}
return nil, errors.New("credential file is unavailable")
}
return os.NewFile(uintptr(fd), fileName), nil
}
View on GitHub (pinned to 5e758547a8)