{"record":{"id":"fb63a428ec776dfe","repo":"siyuan-note/siyuan","slug":"field-s-must-not-be-empty-block-remaining","errorCode":null,"errorMessage":"Field [%s] must not be empty","messagePattern":"Field \\[(.+?)\\] must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/block_remaining.go","lineNumber":92,"sourceCode":"\t\t}\n\t}\n\tif strings.TrimSpace(request.Scope) == \"\" {\n\t\trequest.Scope = \"blocks\"\n\t}\n\treadArray := func(key string, required, nonempty bool) ([]string, error) {\n\t\traw, present := fields[key]\n\t\tif !present && !required {\n\t\t\treturn nil, nil\n\t\t}\n\t\tif !present || bytes.Equal(raw, []byte(\"null\")) {\n\t\t\treturn nil, fmt.Errorf(\"Field [%s] is required\", key)\n\t\t}\n\t\tvar entries []json.RawMessage\n\t\tif json.Unmarshal(raw, &entries) != nil {\n\t\t\treturn nil, fmt.Errorf(\"Field [%s] should be of type [Array]\", key)\n\t\t}\n\t\tif nonempty && len(entries) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"Field [%s] must not be empty\", key)\n\t\t}\n\t\tvar values []string\n\t\tseen := map[string]bool{}\n\t\tfor _, entry := range entries {\n\t\t\tvar value string\n\t\t\tif json.Unmarshal(entry, &value) != nil || strings.TrimSpace(value) == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"Field [%s] should contain non-empty strings\", key)\n\t\t\t}\n\t\t\tif !seen[value] {\n\t\t\t\tvalues = append(values, value)\n\t\t\t\tseen[value] = true\n\t\t\t}\n\t\t}\n\t\treturn values, nil\n\t}\n\tswitch request.Scope {\n\tcase \"blocks\":\n\t\tif request.IDs, err = readArray(\"ids\", true, true); err != nil {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/block_remaining.go#L74-L110","documentation":"For /api/block/checkBlockRef, required array fields (ids under scope \"blocks\", paths under scope \"documents\") must contain at least one element. The kernel decodes the array successfully but rejects it with this error when the nonempty requirement applies and the array length is zero. Optional fields (exactIDs, deletedIDs) are exempt from this check.","triggerScenarios":"POST /api/block/checkBlockRef with scope \"blocks\" and \"ids\": [], or scope \"documents\" with \"paths\": []. Passing \"ids\": [] while relying on the default scope (which defaults to \"blocks\") also triggers it.","commonSituations":"Filtering a block list produced an empty selection that was passed through unchanged; upstream code collected matching IDs into an array that ended up empty; automations that call the check endpoint unconditionally before any selection exists.","solutions":["Ensure ids (or paths for scope \"documents\") contains at least one valid block/document ID before calling","Skip the API call entirely when the caller's selection is empty","If checking optional subsets, move those IDs into exactIDs/deletedIDs and keep ids populated"],"exampleFix":"// before\nawait fetchPost(\"/api/block/checkBlockRef\", { scope: \"blocks\", ids: [] });\n// after\nif (ids.length > 0) {\n  await fetchPost(\"/api/block/checkBlockRef\", { scope: \"blocks\", ids });\n}","handlingStrategy":"validation","validationCode":"if (payload.ids.length === 0) return; // nothing to check\nawait fetchPost('/api/block/checkBlockRef', payload);","typeGuard":"const hasTargets = (p) => Array.isArray(p.ids) && p.ids.length > 0;","tryCatchPattern":"try { await fetchPost('/api/block/checkBlockRef', payload); } catch (e) { if (/must not be empty/.test(e?.message)) return null; throw e; }","preventionTips":["Early-return when the selection list is empty before issuing the request","Never pass through filtered results without a length check","Document that ids/paths are required and non-empty for their scopes"],"tags":["api","validation","empty-array"],"backgroundTag":"empty-required-field","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}