siyuan-note/siyuan · error
only text files inside a skill can be edited
Error message
only text files inside a skill can be edited
What it means
Only text files nested inside a skill directory (path containing "/") may be edited via write/move/remove. Top-level entries under the skills root must be skill directories; attempting to edit a bare top-level file raises this error. It keeps the invariant that the skills root contains only skill directories plus a SKILL.md manifest each.
Solutions
- Move the file into an existing skill directory and edit it there as "<skill>/<file>"
- Create a skill first and place the file inside it
- Remove the stray top-level file if it does not belong in skill storage
Example fix
// before
ManageSkillFiles(SkillFileRequest{Action: "write", Path: "notes.txt", Content: txt, Revision: rev})
// after
ManageSkillFiles(SkillFileRequest{Action: "write", Path: "my-skill/notes.txt", Content: txt, Revision: rev}) Defensive patterns
Strategy: validation
Validate before calling
if !strings.Contains(req.Path, "/") && req.Action != "list" && req.Action != "create" {
return fmt.Errorf("top-level entries are skill directories; edit files as <skill>/<file>")
} Type guard
func isEditableSkillFilePath(p string) bool { return strings.Contains(p, "/") } Try / catch
if err := ManageSkillFiles(req); err != nil && strings.Contains(err.Error(), "only text files inside a skill") {
// relocate the file into a skill directory
} Prevention
- Only issue edit verbs for paths of the form <skill>/<file>
- Keep loose files out of the skills root
- Render top-level entries as non-editable in UIs
When it happens
Trigger: ManageSkillFiles action write/move/remove where request.Path has no "/" (e.g. "notes.txt") and Lstat shows it is not a directory (or is nil): a top-level loose file at the skills root.
Common situations: A stray file was dropped into data/storage/ai/agent/skills and the client tried to edit it like a skill resource; client code did not enforce the skill/<file> path shape.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- create a skill before creating its subdirectories
- rename or delete the skill directory instead of SKILL.md
- unsupported skill operation
- Access to encrypted notebook data is not supported via this…
- AI editor action must not be empty
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/fae3430a8ccb58d6.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/skill_manage.go:381
if request.Action == "read" {
if !info.IsDir() && strings.Contains(request.Path, "/") {
content, readErr := readManagedSkillSource(root, request.Path)
if readErr == nil {
ret.Content = &content
ret.Revision = fmt.Sprintf("%x", sha256.Sum256([]byte(content)))
return ret, nil
}
ret.ReadOnlyReason = skillSourceReadOnlyReason(readErr)
if ret.ReadOnlyReason == "" {
return ret, readErr
}
}
ret.Revision, err = managedSkillRevision(root, request.Path)
return ret, err
}
if info == nil || !info.IsDir() {
if !strings.Contains(request.Path, "/") {
return ret, errors.New("only text files inside a skill can be edited")
}
if info != nil {
if _, err = readManagedSkillSource(root, request.Path); err != nil {
return ret, err
}
}
}
if (request.Action == "move" || request.Action == "remove") && isManagedSkillManifest(request.Path) {
return ret, errors.New("rename or delete the skill directory instead of SKILL.md")
}
if info != nil {
revision, revisionErr := managedSkillRevision(root, request.Path)
if revisionErr != nil {
return ret, revisionErr
}
if request.Revision == "" || request.Revision != revision {
return ret, errors.New("skill changed; reload it before saving, renaming or deleting")
}View on GitHub (pinned to 9f775e8a12)