{"record":{"id":"8ad299905a56602f","repo":"siyuan-note/siyuan","slug":"map-string-json-rawmessage","errorCode":null,"errorMessage":"map[string]json.RawMessage","messagePattern":"map\\[string\\]json\\.RawMessage","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/repo.go","lineNumber":195,"sourceCode":"\tHSize       string      `json:\"hSize\"`       // 格式化好的文件总大小 \"10.00 MB\"\n\tSystemID    string      `json:\"systemID\"`    // 设备 ID\n\tSystemName  string      `json:\"systemName\"`  // 设备名称\n\tSystemOS    string      `json:\"systemOS\"`    // 设备操作系统\n\tTag         string      `json:\"tag\"`         // 索引标记名称\n\tHTagUpdated string      `json:\"hTagUpdated\"` // 标记时间 \"2006-01-02 15:04:05\"\n}\ntype RepoFile struct {\n\tID      string   `json:\"id\"`      // 文件标识\n\tPath    string   `json:\"path\"`    // 文件路径\n\tSize    int64    `json:\"size\"`    // 文件大小\n\tUpdated int64    `json:\"updated\"` // 最后更新时间\n\tChunks  []string `json:\"chunks\"`  // 文件分块列表\n}\n\nfunc repoRequestFields(reader io.Reader, path string) (map[string]json.RawMessage, error) {\n\tfields, err := blockRequestFields(reader, path)\n\tif err != nil {\n\t\treturn nil, errors.New(strings.ReplaceAll(err.Error(), \"map[string]json.RawMessage\", \"map[string]interface {}\"))\n\t}\n\treturn fields, nil\n}\nfunc repoRequestString(fields map[string]json.RawMessage, key string, required, trim bool) (string, error) {\n\tvalue, err := legacyField[string](fields, key, \"String\", required)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif trim && len(fields[key]) > 0 && string(fields[key]) != \"null\" {\n\t\tvalue = strings.TrimSpace(value)\n\t\tif value == \"\" {\n\t\t\treturn \"\", fmt.Errorf(\"Field [%s] must not be empty\", key)\n\t\t}\n\t}\n\treturn value, nil\n}\nfunc init() {\n\tSetRepoIndexRetentionDays.decodeRequest = func(reader io.Reader) (request SetRepoIndexRetentionDaysRequest, err error) {","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/repo.go#L177-L213","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Send a valid JSON object as the request body with Content-Type: application/json","Validate the JSON body parses (e.g. JSON.parse) before calling the API","Check the endpoint's documented request schema and correct field types","Inspect the wrapped underlying error message for the precise decode failure"],"exampleFix":"// before\nfetch('/api/repo/indexRepo', { method: 'POST' }) // empty body\n// after\nfetch('/api/repo/indexRepo', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({}) })","handlingStrategy":"validation","validationCode":"// caller-side (TS)\nfunction isValidJsonObject(body: unknown): body is Record<string, unknown> {\n  if (typeof body !== 'object' || body === null || Array.isArray(body)) return false;\n  try { JSON.parse(typeof body === 'string' ? body : JSON.stringify(body)); return true; } catch { return false; }\n}\nif (!isValidJsonObject(body)) throw new Error('request body must be a JSON object');","typeGuard":"function isRecord(v: unknown): v is Record<string, unknown> { return typeof v === 'object' && v !== null && !Array.isArray(v); }","tryCatchPattern":"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 */ } }","preventionTips":["Always send bodies with Content-Type: application/json","Build bodies with JSON.stringify, never string concatenation","Validate payload shape with a schema before sending","Log the exact body string when a parse error occurs"],"tags":["http","json","request-parsing"],"backgroundTag":"json-decode-failed","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"}