siyuan-note/siyuan · error

Field [notebook] is required

Error message

Field [notebook] is required

What it means

When /api/block/checkBlockRef is called with scope "notebook", the notebook field is mandatory. The kernel raises this error when notebook is absent, an empty string, or an explicit null while scope is "notebook". Scopes "blocks" and "documents" do not require notebook and never hit this branch.

Solutions

  1. Always include the notebook ID string when scope is "notebook"
  2. Guard the call site: only send scope "notebook" after a notebook has been selected
  3. Use scope "blocks" or "documents" instead if the operation is not notebook-wide

Example fix

// before
await fetchPost("/api/block/checkBlockRef", { scope: "notebook", ids });
// after
await fetchPost("/api/block/checkBlockRef", { scope: "notebook", notebook, ids });
Defensive patterns

Strategy: validation

Validate before calling

if (payload.scope === 'notebook' && !payload.notebook) throw new Error('notebook is required when scope is "notebook"');

Type guard

const notebookScopeReady = (p) => p.scope !== 'notebook' || (typeof p.notebook === 'string' && p.notebook.length > 0);

Try / catch

try { await fetchPost('/api/block/checkBlockRef', payload); } catch (e) { if (/Field \[notebook\] is required/.test(e?.message)) { /* prompt for notebook or switch scope */ } }

Prevention

When it happens

Trigger: POST /api/block/checkBlockRef with {"scope":"notebook"} and no notebook key; {"scope":"notebook","notebook":null}; {"scope":"notebook","notebook":""}.

Common situations: Refactoring code from scope "blocks" to "notebook" without adding the notebook parameter; a UI state where no notebook is selected yet the check still fires; null sent because an earlier layer conflated 'absent' with 'nullable'.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/f2ebc999cf40c5e8. Report an issue: GitHub.

Appendix: source

Thrown at kernel/apicontract/block_remaining.go:141

		// 附带块 ID 只参与租约准入,沿用仅收集字符串的规则。
		var entries []json.RawMessage
		if json.Unmarshal(fields["ids"], &entries) == nil {
			for _, entry := range entries {
				var id string
				if !bytes.Equal(entry, []byte("null")) && json.Unmarshal(entry, &id) == nil {
					request.IDs = append(request.IDs, id)
				}
			}
		}
	default:
		return request, errors.New("invalid block ref check scope")
	}
	if raw := fields["notebook"]; len(raw) != 0 && !bytes.Equal(raw, []byte("null")) {
		if json.Unmarshal(raw, &request.Notebook) != nil {
			return request, errors.New("Field [notebook] should be of type [String]")
		}
	} else if request.Scope == "notebook" {
		return request, errors.New("Field [notebook] is required")
	}
	if request.Scope == "notebook" {
		request.Notebook = strings.TrimSpace(request.Notebook)
		if request.Notebook == "" {
			return request, errors.New("Field [notebook] must not be empty")
		}
	}
	_ = json.Unmarshal(fields["id"], &request.ID)
	return
}

func decodeHeadingLevel(reader io.Reader) (request HeadingLevelRequest, err error) {
	fields, err := blockRequestFields(reader, "/api/block/getHeadingLevelTransaction")
	if err != nil {
		return request, err
	}
	if len(fields["level"]) == 0 || bytes.Equal(fields["level"], []byte("null")) || json.Unmarshal(fields["level"], &request.Level) != nil {
		return request, errors.New("Field [level] should be of type [Number]")

View on GitHub (pinned to 9f775e8a12)