{"record":{"id":"558f7c7e66fd9f6d","repo":"jackwener/OpenCLI","slug":"failed-to-parse-xiaoyuzhou-credential-file-file","errorCode":null,"errorMessage":"Failed to parse Xiaoyuzhou credential file: ${filePath}","messagePattern":"Failed to parse Xiaoyuzhou credential file: (.+?)","errorType":"exception","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"clis/xiaoyuzhou/auth.js","lineNumber":62,"sourceCode":"        device_properties: String(raw.device_properties ?? raw.deviceProperties ?? XIAOYUZHOU_DEFAULT_DEVICE_PROPERTIES),\n    };\n}\nexport function loadXiaoyuzhouCredentials() {\n    const filePath = getXiaoyuzhouCredentialFile();\n    if (fs.existsSync(filePath)) {\n        try {\n            const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n            const credentials = normalizeXiaoyuzhouCredentials(parsed);\n            if (!credentials.access_token || !credentials.refresh_token) {\n                throw new ConfigError(`Xiaoyuzhou credential file is missing access_token or refresh_token: ${filePath}`, 'Recreate the file with valid credentials.');\n            }\n            return credentials;\n        }\n        catch (error) {\n            if (error instanceof ConfigError) {\n                throw error;\n            }\n            throw new ConfigError(`Failed to parse Xiaoyuzhou credential file: ${filePath}`, `Ensure ${filePath} contains valid JSON. (${getErrorMessage(error)})`);\n        }\n    }\n    throw new ConfigError(`Missing Xiaoyuzhou credentials. Expected ${filePath}`, `Create ${filePath} with access_token and refresh_token.`);\n}\n\nexport function saveXiaoyuzhouCredentials(credentials) {\n    const filePath = getXiaoyuzhouCredentialFile();\n    fs.mkdirSync(path.dirname(filePath), { recursive: true });\n    fs.writeFileSync(filePath, `${JSON.stringify({\n        access_token: credentials.access_token,\n        refresh_token: credentials.refresh_token,\n        expires_at: credentials.expires_at,\n        device_id: credentials.device_id,\n        device_properties: credentials.device_properties,\n    }, null, 2)}\\n`, 'utf-8');\n}\n\nexport function shouldRefreshXiaoyuzhouCredentials(credentials, now = getNowMs()) {","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoyuzhou/auth.js#L44-L80","documentation":"When reading the xiaoyuzhou credential file fails for any reason other than the missing-token ConfigError, loadXiaoyuzhouCredentials wraps the underlying error in a ConfigError: 'Failed to parse Xiaoyuzhou credential file: <path>' with a hint to ensure the file contains valid JSON (original error appended). Typical cause: fs.readFileSync threw (permissions) or JSON.parse failed on malformed content.","triggerScenarios":"Credential file contains invalid JSON (trailing commas, truncation, HTML error page pasted in); file unreadable due to permissions; encoding issues; ConfigError from the token check is intentionally rethrown unwrapped and does NOT produce this message.","commonSituations":"Editor saved the file as JSON5/with comments; clipboard paste captured extra text; disk-full write left a truncated file; running as a user without read access to the file.","solutions":["Validate the file: run JSON.parse on its contents (or `node -e \"JSON.parse(require('fs').readFileSync('<path>','utf8'))\"`) and fix the syntax error reported","Recreate filePath with clean, complete JSON containing access_token and refresh_token","Check file permissions/readability for the user running the CLI","Read the appended getErrorMessage(error) detail in the hint to distinguish parse vs read failures"],"exampleFix":"// before (trailing comma → parse error)\n{ \"access_token\": \"abc\", \"refresh_token\": \"def\", }\n// after\n{ \"access_token\": \"abc\", \"refresh_token\": \"def\" }","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nfunction assertValidCredJson(filePath) {\n  const raw = fs.readFileSync(filePath, 'utf-8');\n  try { JSON.parse(raw); } catch (e) { throw new Error(`${filePath} is not valid JSON: ${e.message}`); }\n}\nassertValidCredJson('./config/xiaoyuzhou.credentials.json');","typeGuard":null,"tryCatchPattern":"try {\n  const creds = loadXiaoyuzhouCredentials();\n} catch (err) {\n  if (/Failed to parse Xiaoyuzhou credential file/.test(err.message)) {\n    fs.copyFileSync(credPath, credPath + '.bak');\n    await regenerateCredentials(credPath); // rewrite the file as clean JSON\n  } else throw err;\n}","preventionTips":["Write credential files programmatically (JSON.stringify) instead of by hand","Validate the file with JSON.parse during setup scripts","Ensure atomic writes (temp file + rename) so the file is never truncated","Check file read permissions for the executing user"],"tags":["config","json-parse","credentials","xiaoyuzhou"],"backgroundTag":"invalid-credential-file-json","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}