siyuan-note/siyuan · error

document version is required

Error message

document version is required

What it means

Returned by ResolveDocVersionBoxID (history_diff.go:117) when the supplied *DocVersionRef is nil. ResolveDocVersionBoxID walks the ref to find which encrypted notebook a version belongs to, so a nil ref has no type/ID/path to inspect. This is the first guard before the type switch and exists to fail fast on missing input.

Source

Thrown at kernel/model/history_diff.go:117

	signature string
}

type docTextSegment struct {
	node       *ast.Node
	start      int
	end        int
	storedRuns []string
	signature  string
}

type docDiffLCSBudget struct {
	remaining int
}

// ResolveDocVersionBoxID 返回文档版本引用中明确记录的加密笔记本 ID。
func ResolveDocVersionBoxID(ref *DocVersionRef) (string, error) {
	if ref == nil {
		return "", errors.New("document version is required")
	}
	switch ref.Type {
	case docVersionCurrent:
		if !ast.IsNodeIDPattern(ref.ID) {
			return "", errors.New("current document ID is invalid")
		}
		blockTree := treenode.GetBlockTree(ref.ID)
		if blockTree == nil {
			return "", ErrTreeNotFound
		}
		if IsEncryptedBox(blockTree.BoxID) {
			return blockTree.BoxID, nil
		}
		return "", nil
	case docVersionHistory:
		absPath, err := validateHistoryPath(ref.Path)
		if err != nil {
			return "", err

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Ensure the caller always constructs a non-nil DocVersionRef before invoking ResolveDocVersionBoxID or DiffDocVersions.
  2. At the API boundary, reject nil refs with util.ParseJsonArgs/BindJsonArg so the user gets a clear "X document version is required" message before reaching model code (see api/history.go:200-203 for the right-side example).
  3. Default to an empty struct (&DocVersionRef{}) only if downstream code tolerates an empty Type; otherwise surface the missing-field error to the user.

Example fix

// before
boxID, err := ResolveDocVersionBoxID(ref) // ref may be nil

// after
if ref == nil {
    return "", errors.New("document version is required")
}
boxID, err := ResolveDocVersionBoxID(ref)
Defensive patterns

Strategy: validation

Validate before calling

if ref == nil {
    return "", errors.New("document version is required")
}
return ResolveDocVersionBoxID(ref)

Type guard

// validDocVersionRef is a non-nil ref with a known type.
func validDocVersionRef(r *DocVersionRef) bool {
    if r == nil { return false }
    switch r.Type {
    case docVersionCurrent, docVersionHistory, docVersionSnapshot:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: POST /api/history/diffDocVersions or /api/history/rollbackDocHistory invoked with a missing or null "left"/"right" object that deserializes to a nil pointer; a Go caller passing nil instead of an empty DocVersionRef{}.

Common situations: Frontend sends {"left": null, "right": {...}}; a plugin/MCP tool omits the version object; refactoring that introduces an optional pointer without a default.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/fa13efa7f8cadde5. Report an issue: GitHub.