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

  1. Move the file into an existing skill directory and edit it there as "<skill>/<file>"
  2. Create a skill first and place the file inside it
  3. 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

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


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)