{"record":{"id":"0e2a9b1289ec31cc","repo":"siyuan-note/siyuan","slug":"struct-field-sync-s","errorCode":null,"errorMessage":"struct field Sync%s.","messagePattern":"struct field Sync(.+?)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/sync.go","lineNumber":139,"sourceCode":"\tUpdated   string `json:\"updated\"`\n\tCloudName string `json:\"cloudName\"`\n\tSaveDir   string `json:\"saveDir\"`\n}\ntype CloudSyncDirsData struct {\n\tSyncDirs       []*CloudSyncDir `json:\"syncDirs\"`\n\tHSize          string          `json:\"hSize\"`\n\tCheckedSyncDir string          `json:\"checkedSyncDir\"`\n}\ntype SyncAssetDownloadModeData struct {\n\tAssetDownloadMode int `json:\"assetDownloadMode\"`\n}\n\nfunc syncConfigError(err error, name string) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tdetail := strings.ReplaceAll(err.Error(), \"apicontract.Sync\"+name, \"conf.\"+name)\n\treturn errors.New(strings.ReplaceAll(detail, \"struct field Sync\"+name+\".\", \"struct field \"+name+\".\"))\n}\n\n// syncRequestFields 保留整份请求的 JSON 数字解析、重复字段和首个对象读取语义。\nfunc syncRequestFields(reader io.Reader, path string) (fields map[string]json.RawMessage, err error) {\n\tvar raw json.RawMessage\n\terr = json.NewDecoder(reader).Decode(&raw)\n\tif err == nil {\n\t\tfields, err = legacyJSONValue[map[string]json.RawMessage](raw)\n\t}\n\tif err != nil {\n\t\tif errors.Is(err, io.EOF) {\n\t\t\terr = errors.New(\"the request body is empty or truncated (EOF)\")\n\t\t}\n\t\tdetail := strings.ReplaceAll(err.Error(), \"map[string]json.RawMessage\", \"map[string]interface {}\")\n\t\terr = fmt.Errorf(\"Parses request [%s] failed: %s\", path, detail)\n\t}\n\treturn\n}","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/sync.go#L121-L157","documentation":"This rewritten error is produced by syncConfigError when decoding POST /api/sync/setSyncProviderS3, /setSyncProviderWebDAV, or /setSyncProviderLocal. The raw reflection error from unmarshalling the nested provider config mentions internal names like `struct field SyncS3.Endpoint`; the rewriter normalizes them to user-facing config names (`struct field S3.Endpoint`). The underlying problem is one field inside the provider object has the wrong JSON type.","triggerScenarios":"POSTing /api/sync/setSyncProviderS3 with e.g. {\"s3\":{\"timeout\":\"30\"}} (string where number expected) or {\"s3\":{\"skipTlsVerify\":\"yes\"}}; same for webdav/local nested objects.","commonSituations":"Config copied from a YAML/INI export where numbers became strings; a settings UI storing values as strings and forwarding them unconverted; API version drift between client payload shape and kernel struct.","solutions":["Check the exact field named in the message and send the correct JSON type (numbers as numbers, booleans as booleans)","Compare the payload against the documented SyncS3/SyncWebDAV/SyncLocal object shape","Read the request.ConfigError() on the returned object — for these endpoints the error is attached to the response, not a hard failure"],"exampleFix":"// before\n{\"s3\": {\"endpoint\": \"https://s3.example.com\", \"timeout\": \"30\"}}\n// after\n{\"s3\": {\"endpoint\": \"https://s3.example.com\", \"timeout\": 30}}","handlingStrategy":"validation","validationCode":"const num = (v) => typeof v === 'number' && !isNaN(v); const validS3 = s => s && typeof s === 'object' && (s.timeout === undefined || num(s.timeout)) && (s.concurrentReqs === undefined || num(s.concurrentReqs));","typeGuard":"const isProviderConfig = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);","tryCatchPattern":"const req = await fetchPost('/api/sync/setSyncProviderS3', {s3: cfg}); if (req.code !== 0 || (req.data && req.data.configError)) console.error(req.msg);","preventionTips":["Keep numbers as JSON numbers, booleans as JSON booleans","Compare payload to the documented SyncS3/SyncWebDAV/SyncLocal schema","Avoid stringifying config values in settings UIs before sending"],"tags":["json","sync","config","type-mismatch"],"backgroundTag":"config-type-mismatch","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"}