t8y2/dbx · error
Oracle TNS_ADMIN must be a directory containing tnsnames.ora
Error message
Oracle TNS_ADMIN must be a directory containing tnsnames.ora: %s
What it means
After the TNS_ADMIN path stats successfully, oracleTNSNamesPath checks that it is actually a directory (info.IsDir). A file (e.g. someone set TNS_ADMIN to tnsnames.ora itself) fails this check with this message.
Source
Thrown at agents/drivers/oracle-go/tns.go:93
aliases, err := readOracleTNSAliases(tnsNamesPath, make(map[string]bool), 0)
if err != nil {
return "", err
}
descriptor, ok := aliases[strings.ToUpper(config.Alias)]
if !ok {
return "", fmt.Errorf("Oracle TNS alias %q was not found in %s", config.Alias, tnsNamesPath)
}
return descriptor, nil
}
func oracleTNSNamesPath(tnsAdmin string) (string, error) {
path := filepath.Clean(strings.TrimSpace(tnsAdmin))
info, err := os.Stat(path)
if err != nil {
return "", fmt.Errorf("Oracle TNS_ADMIN directory is not accessible: %s", path)
}
if !info.IsDir() {
return "", fmt.Errorf("Oracle TNS_ADMIN must be a directory containing tnsnames.ora: %s", path)
}
tnsNamesPath := filepath.Join(path, "tnsnames.ora")
if info, err := os.Stat(tnsNamesPath); err != nil || info.IsDir() {
return "", fmt.Errorf("Oracle tnsnames.ora was not found in TNS_ADMIN directory: %s", path)
}
return tnsNamesPath, nil
}
func readOracleTNSAliases(path string, visited map[string]bool, depth int) (map[string]string, error) {
if depth > 8 {
return nil, fmt.Errorf("Oracle TNS include depth exceeds 8 files")
}
absolutePath, err := filepath.Abs(path)
if err != nil {
return nil, fmt.Errorf("Failed to resolve Oracle TNS file path: %w", err)
}
if visited[absolutePath] {
return map[string]string{}, nilView on GitHub (pinned to c0390bff16)
Solutions
- Set TNS_ADMIN to the directory containing tnsnames.ora, not the file itself: /opt/oracle/network/admin not /opt/oracle/network/admin/tnsnames.ora.
- Verify with ls -ld that the path is a directory (d in permissions).
- Fix any config templating that appends 'tnsnames.ora' to the value.
- Check symlinks resolve to a directory (readlink -f <path>).
Example fix
// before dsn := "jdbc:oracle:thin:@ALIAS?TNS_ADMIN=/opt/oracle/network/admin/tnsnames.ora" // file path // after dsn := "jdbc:oracle:thin:@ALIAS?TNS_ADMIN=/opt/oracle/network/admin"
Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(tnsAdmin)
if err == nil && !info.IsDir() {
return errors.New("TNS_ADMIN must be the directory containing tnsnames.ora, not the file itself")
} Try / catch
if strings.Contains(err.Error(), "must be a directory") {
// strip a trailing tnsnames.ora component and retry once
dir := filepath.Dir(tnsAdmin)
} Prevention
- Configure TNS_ADMIN as a directory path, never a file path.
- Use filepath.Dir if you only have the tnsnames.ora path.
- Verify with os.Stat + IsDir in config validation at startup.
When it happens
Trigger: buildDSNForConnect -> resolveOracleTNSAlias -> oracleTNSNamesPath is given a TNS_ADMIN value pointing at a regular file rather than the directory that contains tnsnames.ora.
Common situations: Setting TNS_ADMIN to the full path of tnsnames.ora instead of its containing directory; a symlink resolving to a file; config tools substituting a file path variable where a directory was expected.
Related errors
- Oracle TNS_ADMIN directory is not accessible: %s
- Oracle tnsnames.ora was not found in TNS_ADMIN directory: %s
- Oracle TNS network alias is invalid
- Oracle TNS_ADMIN directory is required
- Oracle TNS connection parameters are invalid: %w
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/54897847d6860578.
Report an issue: GitHub.