siyuan-note/siyuan · error

map[string]json.RawMessage

Error message

map[string]json.RawMessage

What it means

repoRequestFields wraps blockRequestFields and post-processes any error by rewriting the Go type name map[string]json.RawMessage in the message to the friendlier map[string]interface {}. This error text signals that the raw request body could not be decoded into the expected JSON object shape for a repo API endpoint.

Solutions

  1. Send a valid JSON object as the request body with Content-Type: application/json
  2. Validate the JSON body parses (e.g. JSON.parse) before calling the API
  3. Check the endpoint's documented request schema and correct field types
  4. Inspect the wrapped underlying error message for the precise decode failure

Example fix

// before
fetch('/api/repo/indexRepo', { method: 'POST' }) // empty body
// after
fetch('/api/repo/indexRepo', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({}) })
Defensive patterns

Strategy: validation

Validate before calling

// caller-side (TS)
function isValidJsonObject(body: unknown): body is Record<string, unknown> {
  if (typeof body !== 'object' || body === null || Array.isArray(body)) return false;
  try { JSON.parse(typeof body === 'string' ? body : JSON.stringify(body)); return true; } catch { return false; }
}
if (!isValidJsonObject(body)) throw new Error('request body must be a JSON object');

Type guard

function isRecord(v: unknown): v is Record<string, unknown> { return typeof v === 'object' && v !== null && !Array.isArray(v); }

Try / catch

try { const res = await fetch(url, opts); const data = await res.json(); } catch (e) { if (String(e).includes('Parses request') || String(e).includes('map[string]')) { /* re-send valid JSON object */ } }

Prevention

When it happens

Trigger: Calling any repo API endpoint (via repoRequestFields at kernel/apicontract/repo.go:195) with a request body that is not a valid JSON object — e.g. malformed JSON, a JSON array/string instead of an object, or an empty body — so blockRequestFields fails decoding into map[string]json.RawMessage.

Common situations: Clients sending form-encoded or missing Content-Type bodies, hand-built HTTP requests with truncated JSON, or older clients that sent map[string]interface {} style payloads that no longer parse.

Related errors


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

Appendix: source

Thrown at kernel/apicontract/repo.go:195

	HSize       string      `json:"hSize"`       // 格式化好的文件总大小 "10.00 MB"
	SystemID    string      `json:"systemID"`    // 设备 ID
	SystemName  string      `json:"systemName"`  // 设备名称
	SystemOS    string      `json:"systemOS"`    // 设备操作系统
	Tag         string      `json:"tag"`         // 索引标记名称
	HTagUpdated string      `json:"hTagUpdated"` // 标记时间 "2006-01-02 15:04:05"
}
type RepoFile struct {
	ID      string   `json:"id"`      // 文件标识
	Path    string   `json:"path"`    // 文件路径
	Size    int64    `json:"size"`    // 文件大小
	Updated int64    `json:"updated"` // 最后更新时间
	Chunks  []string `json:"chunks"`  // 文件分块列表
}

func repoRequestFields(reader io.Reader, path string) (map[string]json.RawMessage, error) {
	fields, err := blockRequestFields(reader, path)
	if err != nil {
		return nil, errors.New(strings.ReplaceAll(err.Error(), "map[string]json.RawMessage", "map[string]interface {}"))
	}
	return fields, nil
}
func repoRequestString(fields map[string]json.RawMessage, key string, required, trim bool) (string, error) {
	value, err := legacyField[string](fields, key, "String", required)
	if err != nil {
		return "", err
	}
	if trim && len(fields[key]) > 0 && string(fields[key]) != "null" {
		value = strings.TrimSpace(value)
		if value == "" {
			return "", fmt.Errorf("Field [%s] must not be empty", key)
		}
	}
	return value, nil
}
func init() {
	SetRepoIndexRetentionDays.decodeRequest = func(reader io.Reader) (request SetRepoIndexRetentionDaysRequest, err error) {

View on GitHub (pinned to 9f775e8a12)