larksuite/cli · error
returned nil without an error
Error message
returned nil without an error
What it means
errorsNilResult ('returned nil without an error') is the sentinel for a plugin filesystem contract violation: the plugin's fs.FS/fs.File/fs.DirEntry/fs.FileInfo method returned a nil result with a nil error, which the fs interfaces forbid. The wrapper turns this into an fs.PathError so callers never receive a nil file/info that would panic later.
Source
Thrown at internal/skillpolicy/pluginfs.go:129
}
return safe, nil
}
func (p *pluginFS) recoverPath(op, path string, err *error) {
if value := recover(); value != nil {
*err = p.pathError(op, path, fmt.Errorf("panic: %v", value))
}
}
func (p *pluginFS) pathError(op, path string, cause error) error {
return &fs.PathError{
Op: op,
Path: path,
Err: fmt.Errorf("plugin %q %s filesystem: %w", p.owner, p.field, cause),
}
}
var errorsNilResult = fmt.Errorf("returned nil without an error")
func joinPath(parent, child string) string {
if parent == "." {
return child
}
return parent + "/" + child
}
type pluginFile struct {
fsys *pluginFS
path string
source fs.File
}
func (f *pluginFile) Stat() (info fs.FileInfo, err error) {
defer func() {
if value := recover(); value != nil {
info = nilView on GitHub (pinned to 7fd6ef3c07)
Solutions
- Fix the plugin FS to return a real error (e.g. &fs.PathError{Op:..., Err: fs.ErrNotExist}) instead of (nil, nil).
- As a caller, read the fs.PathError — the plugin owner and op fields identify the offending method.
- Add contract tests to the plugin: every method must return non-nil results or a non-nil error.
- If using a mock/test FS in the plugin, implement it with fstest.MapFS to guarantee correct semantics.
Example fix
// before (plugin FS, contract violation)
func (f myFS) Open(name string) (fs.File, error) {
file, ok := f.files[name]
if !ok {
return nil, nil // forbidden: nil, nil
}
return file, nil
}
// after
func (f myFS) Open(name string) (fs.File, error) {
file, ok := f.files[name]
if !ok {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
return file, nil
} Defensive patterns
Strategy: validation
Validate before calling
// before relying on a custom plugin FS, verify contract compliance
if err := fstest.TestFS(pluginFSInstance, "my-skill/SKILL.md"); err != nil {
return fmt.Errorf("plugin FS violates fs.FS contract: %w", err)
} Try / catch
if err != nil {
if strings.Contains(err.Error(), "returned nil without an error") {
return fmt.Errorf("plugin FS returned nil,nil — plugin bug: %w", err)
}
return err
} Prevention
- Implement plugin FS with fstest.MapFS when possible.
- Never return (nil, nil) from any fs.FS/fs.File/fs.DirEntry/fs.FileInfo method.
- Add fstest.TestFS checks to the plugin's test suite.
- Review custom FileInfo wrappers for nil Info returns.
When it happens
Trigger: pluginFS.Open gets (nil, nil) from the plugin (pluginfs.go:45); Stat gets a nil FileInfo (pluginfs.go:66); ReadDir yields a nil DirEntry (pluginfs.go:98); pluginFile.Stat or pluginDirEntry.Info get nil FileInfo with nil error.
Common situations: A hand-rolled plugin FS returns nil,nil on a not-found path instead of fs.ErrNotExist; a generated or mock FS in a plugin forgets to return an error; a plugin FileInfo wrapper returns nil Info for its dir entries.
Related errors
- plugin %q %s filesystem: %w
- resolve save path: empty result for %q
- Invalid column: {column!r}
- Invalid column index: {index}
- anchor outside sheet: {position!r}
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/1c2484392342040a.
Report an issue: GitHub.