gastownhall/beads · error
agents file must be a simple filename without path separator
Error message
agents file must be a simple filename without path separators, got %q
What it means
ValidateAgentsFile requires a plain filename: any '/' or '\\' means the caller passed a path rather than a simple name. This restriction lets the write layer control where agents files live and simplifies symlink/security checks. The offending name is quoted in the message.
Source
Thrown at internal/config/config.go:1176
debug.Logf("config: agents.file %q failed validation (%v), using default", name, err)
return DefaultAgentsFile
}
return name
}
// ValidateAgentsFile checks that filename is safe to use as an agents file path.
// It rejects absolute paths, path separators, names longer than 255 characters,
// and non-markdown extensions. This is a pure string validation function — I/O
// checks (e.g. symlink detection) are deferred to the file write layer.
func ValidateAgentsFile(filename string) error {
if filename == "" {
return fmt.Errorf("agents file name must not be empty")
}
if len(filename) > 255 {
return fmt.Errorf("agents file name exceeds 255 characters")
}
if strings.ContainsAny(filename, "/\\") {
return fmt.Errorf("agents file must be a simple filename without path separators, got %q", filename)
}
ext := strings.ToLower(filepath.Ext(filename))
if ext != ".md" {
return fmt.Errorf("agents file must have .md extension, got %q", ext)
}
return nil
}
// getConfigList retrieves a list-typed configuration value from config.yaml,
// accepting either the YAML list form (e.g. `types: { custom: [step, wisp] }`)
// or the legacy comma-separated string form (e.g.
// `types.custom = "step,wisp"`). Entries are trimmed; empty entries are
// dropped. The dual-form support is required for project-extension
// types/statuses declared in .beads/config.yaml — see gastownhall/beads#4024.
func getConfigList(key string) []string {
if v == nil {
debug.Logf("config: viper not initialized, returning nil for key %q", key)
return nilView on GitHub (pinned to 71377f2769)
Solutions
- Pass only the base filename, e.g. "AGENTS.md", and let the library place it in the beads directory.
- Use filepath.Base on the configured value at the call site before validation.
- Update the config key to remove any directory portion.
Example fix
// before
config.SafeAgentsFile("docs/AGENTS.md")
// after
config.SafeAgentsFile(filepath.Base("docs/AGENTS.md")) // "AGENTS.md" Defensive patterns
Strategy: validation
Validate before calling
if strings.ContainsAny(name, "/\\") {
return fmt.Errorf("pass a bare filename, not a path: %q", name)
}
name = filepath.Base(name) // normalize before calling the library Try / catch
if err := config.SafeAgentsFile(name); err != nil {
if strings.Contains(err.Error(), "path separators") {
log.Warn("agents file must be a bare name; using base", "got", name)
return config.SafeAgentsFile(filepath.Base(name))
}
return err
} Prevention
- Normalize user-supplied values with filepath.Base before validation.
- Document in config comments that the key takes a filename, not a path.
- Sanitize Windows separators when importing configs from Windows machines.
- Treat path-like values in this setting as a config smell worth warning about.
When it happens
Trigger: Calling ValidateAgentsFile or SafeAgentsFile with values like "docs/AGENTS.md", "/etc/agents.md", "sub\\dir.md", or any name containing a path separator.
Common situations: A user sets an absolute path in the agents-file config key; a script builds a relative path; Windows-style separators leak in from cross-platform tooling.
Related errors
- agents file name must not be empty
- agents file name exceeds 255 characters
- agents file must have .md extension, got %q
- server: NewDoltServer: doltBinExec is required
- server: NewDoltServer: rootDir is required
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/4b418a9b4f49e713.
Report an issue: GitHub.