Molunerfinn/PicGo · error
PICGO_CLOUD_CONFIG_SYNC_LOCAL_CONFIG_INVALID
Error message
PICGO_CLOUD_CONFIG_SYNC_LOCAL_CONFIG_INVALID
What it means
readLocalConfigWithComments parses ~/.picgo/config.json with a comment-preserving parser and requires the result to be a plain object before it is used for PicGo Cloud config sync. If the file parses to a non-object (array, string, number, null, or garbage), it throws PICGO_CLOUD_CONFIG_SYNC_LOCAL_CONFIG_INVALID. This protects the sync flow from operating on a structurally broken local config.
Source
Thrown at src/main/events/rpc/routes/cloud.ts:140
}
const buildConfigSyncState = async (): Promise<IPicGoCloudConfigSyncState> => {
return {
sessionStatus: configSyncSessionStatus,
encryptionMethod: getLocalEncryptionMethod(),
lastSyncedAt: await getSnapshotUpdatedAt(),
conflicts: configSyncSessionStatus === IPicGoCloudConfigSyncSessionStatus.CONFLICT ? configSyncConflictItems : undefined
}
}
const readLocalConfigWithComments = async (): Promise<IConfig> => {
if (!(await fs.pathExists(picgo.configPath))) {
return picgo.getConfig<IConfig>()
}
const content = await fs.readFile(picgo.configPath, 'utf8')
const parsed: unknown = parse(content)
if (!isPlainObject(parsed)) {
throw new Error(T('PICGO_CLOUD_CONFIG_SYNC_LOCAL_CONFIG_INVALID'))
}
return parsed as IConfig
}
const extractConflictItems = (diffTree: IDiffNode): IPicGoCloudConfigSyncConflictItem[] => {
const items: IPicGoCloudConfigSyncConflictItem[] = []
const walk = (node: IDiffNode, pathSegments: string[]) => {
const nextSegments = node.key === 'root' ? pathSegments : [...pathSegments, node.key]
if (node.status === ConflictType.CONFLICT) {
// If the conflict is an object-level aggregation, surface leaf conflicts instead.
if (node.children && node.children.length > 0) {
node.children.forEach(child => walk(child, nextSegments))
return
}
items.push({View on GitHub (pinned to 07ec7068a5)
Solutions
- Open picgo.configPath (default ~/.picgo/config.json) and make it a valid JSON object, e.g. {}
- Back up and delete/recreate the config file, letting PicGo regenerate defaults
- Validate the file with JSON.parse (after stripping comments) before triggering cloud sync
- Restore the config from a backup or re-configure uploaders via the UI
Example fix
// before
~/.picgo/config.json => "just a string"
// after
~/.picgo/config.json => { "picBed": { "current": "github" } } Defensive patterns
Strategy: type-guard
Validate before calling
const raw = JSON.parse(stripJsonComments(fs.readFileSync(configPath, 'utf8')))
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
throw new Error('config.json must be a JSON object')
} Type guard
function isPlainObject(v) {
return typeof v === 'object' && v !== null && !Array.isArray(v)
} Try / catch
try {
await startCloudSync()
} catch (e) {
if (e.message.includes('LOCAL_CONFIG_INVALID')) {
await backupAndResetConfigFile(configPath)
} else throw e
} Prevention
- Keep config.json a top-level JSON object; never write null/arrays
- Back up ~/.picgo/config.json before hand-editing
- Validate the file after any external tool touches it
- Shut down PicGo cleanly to avoid truncated writes
When it happens
Trigger: Starting a cloud config sync when picgo.configPath exists but its content is not a JSON object — e.g. the file contains 'null', '[1,2]', a bare string, or content that a comment-tolerant parser resolves to a non-object.
Common situations: Config file truncated/corrupted by a crash or disk-full during write; user hand-edited config.json into invalid or non-object JSON; another tool overwrote the PicGo config with a different format.
Related errors
- TIPS_UPLOADER_CONFIG_CANNOT_DELETE_LAST
- TIPS_UPLOADER_CONFIG_NAME_EMPTY
- TIPS_UPLOADER_CONFIG_NOT_FOUND
- Failed to change current uploader
- Failed to load provider configs
AI-assisted analysis of Molunerfinn/PicGo@07ec7068a5 (2026-08-30).
Data as JSON: /api/errors/22944fe9a0de2a6f.
Report an issue: GitHub.