siyuan-note/siyuan · error

Field [ids] should contain strings

Error message

Field [ids] should contain strings

What it means

In /api/block/getHeadingLevelTransaction, the optional ids array is validated element-by-element: every element must be a JSON string (null elements are also rejected, unlike some other endpoints). The kernel raises this error when any element fails to unmarshal into a string. This branch only runs when ids is present and is a valid JSON array.

Solutions

  1. Ensure every element is a plain JSON string block ID
  2. Filter or map non-strings: ids.filter(id => typeof id === 'string' && id)
  3. If a single block is targeted, use the scalar "id" field instead of the ids array

Example fix

// before
await fetchPost("/api/block/getHeadingLevelTransaction", { ids: [id1, id2], level: 2 }); // id2 is a number
// after
await fetchPost("/api/block/getHeadingLevelTransaction", { ids: [id1, String(id2)], level: 2 });
Defensive patterns

Strategy: validation

Validate before calling

if (payload.ids && !payload.ids.every(id => typeof id === 'string' && id)) throw new TypeError('ids must all be non-empty strings');

Type guard

const isBlockIdArray = (v) => Array.isArray(v) && v.every((x) => typeof x === 'string' && /^\d{14}-[0-9a-z]{7}$/.test(x));

Try / catch

try { await fetchPost('/api/block/getHeadingLevelTransaction', payload); } catch (e) { if (/Field \[ids\] should contain strings/.test(e?.message)) { payload.ids = payload.ids.filter(id => typeof id === 'string' && id); /* retry */ } }

Prevention

When it happens

Trigger: POST /api/block/getHeadingLevelTransaction with "ids": ["id1", 42], "ids": [null], or "ids": [{"id":"..."}].

Common situations: Mixing numeric row IDs from a database view with block ID strings; null elements appearing after JSON round-trips; passing objects copied from another API's response shape.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at kernel/apicontract/block_remaining.go:168

	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]")
	}
	var entries []json.RawMessage
	if raw := fields["ids"]; len(raw) > 0 && !bytes.Equal(raw, []byte("null")) && json.Unmarshal(raw, &entries) == nil {
		request.IDs = []string{}
		seen := map[string]bool{}
		for _, entry := range entries {
			var id string
			if bytes.Equal(entry, []byte("null")) || json.Unmarshal(entry, &id) != nil {
				return request, errors.New("Field [ids] should contain strings")
			}
			if !seen[id] {
				request.IDs = append(request.IDs, id)
				seen[id] = true
			}
		}
	} else if len(fields["id"]) == 0 || bytes.Equal(fields["id"], []byte("null")) || json.Unmarshal(fields["id"], &request.ID) != nil {
		return request, errors.New("Field [id] should be of type [String]")
	}
	return
}

View on GitHub (pinned to 9f775e8a12)