iflytek/astron-agent · error
credential file changed while being opened
Error message
credential file changed while being opened
What it means
After opening, openCredentialFileNoFollow re-stats the open file and compares it (os.SameFile) with the pre-open Lstat result. If the stat fails, the file is no longer a regular file, or the inode changed, it concludes the credential file was replaced or modified during the open window and refuses to return the handle. This closes the TOCTOU race where a symlink/regular-file swap happens between check and open.
Solutions
- Retry the startup once the credential rotation completes; make rotation tools pause or signal the service after writing
- Use atomic write conventions that finish before the service reads (write temp + rename before service start), or restart the service after rotation
- Serialize credential deployment with service startup (dependency ordering in systemd/compose)
- Check for competing processes (two rotation agents) writing the same path
Example fix
// before * * * * * /usr/bin/fetch-secret > /etc/tenant/credential.key # overwrites in place, races readers // after * * * * * /usr/bin/fetch-secret > /etc/tenant/credential.key.tmp && mv /etc/tenant/credential.key.tmp /etc/tenant/credential.key && systemctl try-restart tenant
Defensive patterns
Strategy: retry
Validate before calling
// No reliable pre-check can prevent an open-window swap; instead, after opening, // confirm stability: info1, _ := os.Stat(path) info2, _ := os.Open(path) // then stat fd and os.SameFile(info1, fdInfo)
Try / catch
var f *os.File
err := retry.Do(func() error {
var err error
f, err = openCredentialFileNoFollow(path)
return err
}, retry.Attempts(3), retry.Delay(100*time.Millisecond))
if err != nil {
return fmt.Errorf("credential file kept changing during open (rotation race?): %w", err)
} Prevention
- Schedule secret rotation outside service startup windows, or restart the service after rotation
- Use write-temp-then-rename atomic updates in rotation tooling
- Serialize deployment steps: finish credential writes before starting the service
- Add retry-with-backoff around credential opening at startup
When it happens
Trigger: File replaced (rename/unlink+create) between Lstat and Open; Stat on the open fd fails; the opened fd points at a non-regular file; hard link swapped to a different inode mid-open.
Common situations: Secret rotation tools (vault-agent, consul-template, k8s secret remount) atomically replacing the credential file exactly while the service is starting; concurrent deploys writing the credential file; automated config sync jobs racing service startup.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- credential file cannot be inspected
- credential file must be a regular non-symbolic-link file
- credential file is too large
- credential file cannot be read
- credential file is unavailable
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/4234ef704a01ebec.
Report an issue: GitHub.
Appendix: source
Thrown at core/tenant/config/credential_file_other.go:29
// O_NOFOLLOW. Supported production images use the Unix implementation above.
func openCredentialFileNoFollow(fileName string) (*os.File, error) {
pathInfo, err := os.Lstat(fileName)
if err != nil {
return nil, errors.New("credential file is unavailable")
}
if pathInfo.Mode()&os.ModeSymlink != 0 || !pathInfo.Mode().IsRegular() {
return nil, errors.New(
"credential file must be a regular non-symbolic-link file",
)
}
file, err := os.Open(fileName)
if err != nil {
return nil, errors.New("credential file is unavailable")
}
openedInfo, err := file.Stat()
if err != nil || !openedInfo.Mode().IsRegular() || !os.SameFile(pathInfo, openedInfo) {
_ = file.Close()
return nil, errors.New("credential file changed while being opened")
}
return file, nil
}
View on GitHub (pinned to 5e758547a8)