{"record":{"id":"bd16a4a3622c705d","repo":"docmirror/dev-sidecar","slug":"error-bd16a4","errorCode":null,"errorMessage":"远程配置对象为空:","messagePattern":"远程配置对象为空:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/core/src/config-api.js","lineNumber":98,"sourceCode":"          try {\n            remoteConfig = jsonApi.parse(body)\n          } catch {\n            log.error(`远程配置内容格式不正确, url: ${remoteConfigUrl}, body: ${body}`)\n            remoteConfig = null\n          }\n\n          if (remoteConfig != null) {\n            const remoteSavePath = configLoader.getRemoteConfigPath(suffix)\n            try {\n              fs.writeFileSync(remoteSavePath, body)\n              log.info('保存远程配置文件成功:', remoteSavePath)\n            } catch (e) {\n              log.error('保存远程配置文件失败:', remoteSavePath, ', error:', e)\n              reject(new Error(`保存远程配置文件失败: ${e.message}`))\n              return\n            }\n          } else {\n            log.warn('远程配置对象为空:', remoteConfigUrl)\n          }\n\n          resolve()\n        } else {\n          log.error(`下载远程配置失败: ${remoteConfigUrl}, response:`, response, ', body:', body)\n\n          let message\n          if (response) {\n            message = `下载远程配置失败: ${remoteConfigUrl}, message: ${response.statusMessage}, code: ${response.statusCode}`\n          } else {\n            message = `下载远程配置失败: response: ${response}`\n          }\n          reject(new Error(message))\n        }\n      })\n    })\n  },\n  deleteRemoteConfigFile (suffix = '') {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/docmirror/dev-sidecar/blob/7710cd56cce760c708f30b01d2d4056eb8c402d5/packages/core/src/config-api.js#L80-L116","documentation":"This is a warning logged (not a thrown Error) inside doDownloadRemoteConfig of config-api.js. It means the remote config URL returned HTTP 200 with a body, but after JSON5 parsing the result was null/empty — i.e. the downloaded content is not a usable config object. The library intentionally does not overwrite or reject: it skips saving the remote config file and resolves so startup continues with whatever config exists locally.","triggerScenarios":"doDownloadRemoteConfig fetched app.remoteConfig.url (or personalUrl) and got a 200 response, but jsonApi.parse(body) yielded null — e.g. body was whitespace/HTML stripped to nothing, a parse fallback produced null, or the server returned an empty-ish document that passed the body.length<2 check but parsed to null.","commonSituations":"The remote config URL points at an HTML page instead of raw JSON/JSON5 (e.g. a GitHub HTML view rather than raw.githubusercontent.com); the file on the remote repo was emptied or renamed; a captive portal or CDN returns 200 with a stub page; a shared-config gist was deleted and the host serves an empty 200 page.","solutions":["Open the remote config URL in a browser or curl it and verify the body is valid JSON/JSON5 (an object, not HTML or empty text).","Fix the app.remoteConfig.url in your config to point at the raw JSON/JSON5 file (for GitHub use raw.githubusercontent.com URLs).","If you intentionally disabled the remote config, restore its content upstream or set app.remoteConfig.enabled=false.","Check for intermediaries (proxy/portal) returning 200 HTML stubs; bypass them or correct DNS/SNI settings."],"exampleFix":"// before\napp: { remoteConfig: { enabled: true, url: 'https://github.com/user/config/blob/main/shared.json5' } }\n// after\napp: { remoteConfig: { enabled: true, url: 'https://raw.githubusercontent.com/user/config/main/shared.json5' } }","handlingStrategy":"validation","validationCode":"const res = await fetch(url)\nconst text = await res.text()\nlet cfg = null\ntry { cfg = JSON.parse(text) } catch {}\nif (res.ok && cfg && typeof cfg === 'object' && Object.keys(cfg).length > 0) {\n  // safe to use as remote config\n} else {\n  console.warn('remote config empty/invalid, keeping local config:', url)\n}","typeGuard":"function isValidConfigObject(v) {\n  return v != null && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length > 0\n}","tryCatchPattern":null,"preventionTips":["Point remoteConfig.url at raw JSON/JSON5 endpoints, never HTML pages.","After editing the shared config upstream, curl the URL to confirm it still parses as an object.","Set app.remoteConfig.enabled=false when no valid remote source exists.","Keep a valid local ~/.dev-sidecar/config.json so a bad remote fetch degrades gracefully."],"tags":["remote-config","config-download","json-parse","warning"],"backgroundTag":"remote-config-empty-body","analyzedSha":"7710cd56cce760c708f30b01d2d4056eb8c402d5","analyzedAt":"2026-08-31T22:07:07.234Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}