jackwener/OpenCLI · error · ConfigError
Xiaoyuzhou credential file is missing access_token or refres
Error message
Xiaoyuzhou credential file is missing access_token or refresh_token: ${filePath} What it means
loadXiaoyuzhouCredentials found the credential JSON file at filePath and parsed it, but after normalization either access_token or refresh_token was missing/empty, so it throws ConfigError with a remediation hint ('Recreate the file with valid credentials.'). The library requires both tokens to refresh or authorize xiaoyuzhou (小宇宙) API requests.
Source
Thrown at clis/xiaoyuzhou/auth.js:54
if (!expiresAt && lastUpdatedTs > 0) {
expiresAt = lastUpdatedTs * 1000 + XIAOYUZHOU_TOKEN_TTL_MS;
}
return {
access_token: String(raw.access_token ?? raw.accessToken ?? '').trim(),
refresh_token: String(raw.refresh_token ?? raw.refreshToken ?? '').trim(),
expires_at: expiresAt,
device_id: String(raw.device_id ?? raw.deviceId ?? XIAOYUZHOU_DEFAULT_DEVICE_ID).trim() || XIAOYUZHOU_DEFAULT_DEVICE_ID,
device_properties: String(raw.device_properties ?? raw.deviceProperties ?? XIAOYUZHOU_DEFAULT_DEVICE_PROPERTIES),
};
}
export function loadXiaoyuzhouCredentials() {
const filePath = getXiaoyuzhouCredentialFile();
if (fs.existsSync(filePath)) {
try {
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
const credentials = normalizeXiaoyuzhouCredentials(parsed);
if (!credentials.access_token || !credentials.refresh_token) {
throw new ConfigError(`Xiaoyuzhou credential file is missing access_token or refresh_token: ${filePath}`, 'Recreate the file with valid credentials.');
}
return credentials;
}
catch (error) {
if (error instanceof ConfigError) {
throw error;
}
throw new ConfigError(`Failed to parse Xiaoyuzhou credential file: ${filePath}`, `Ensure ${filePath} contains valid JSON. (${getErrorMessage(error)})`);
}
}
throw new ConfigError(`Missing Xiaoyuzhou credentials. Expected ${filePath}`, `Create ${filePath} with access_token and refresh_token.`);
}
export function saveXiaoyuzhouCredentials(credentials) {
const filePath = getXiaoyuzhouCredentialFile();
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, `${JSON.stringify({
access_token: credentials.access_token,View on GitHub (pinned to 49907e53dc)
Solutions
- Open filePath and confirm both access_token and refresh_token keys exist with non-empty string values
- Re-create the file with a complete credential export from the xiaoyuzhou source (both tokens)
- Check key naming/casing matches what normalizeXiaoyuzhouCredentials expects (refresh_token vs refreshToken)
- Re-run the auth/login flow to regenerate fresh tokens and save them via saveXiaoyuzhouCredentials
Example fix
// before (credentials.json)
{ "access_token": "abc123" }
// after
{ "access_token": "abc123", "refresh_token": "def456" } Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function validateXiaoyuzhouCreds(filePath) {
if (!fs.existsSync(filePath)) throw new Error(`missing ${filePath}`);
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
if (!parsed.access_token || !parsed.refresh_token) {
throw new Error(`${filePath} must contain non-empty access_token and refresh_token`);
}
}
validateXiaoyuzhouCreds('./config/xiaoyuzhou.credentials.json'); Type guard
function hasBothTokens(c) {
return typeof c?.access_token === 'string' && c.access_token.length > 0
&& typeof c?.refresh_token === 'string' && c.refresh_token.length > 0;
} Try / catch
try {
const creds = loadXiaoyuzhouCredentials();
} catch (err) {
if (/missing access_token or refresh_token/.test(err.message)) {
await runXiaoyuzhouAuthFlow(); // regenerate and save both tokens
} else throw err;
} Prevention
- Never hand-edit credential files; generate them via the auth flow
- Validate both token fields in CI before jobs that use xiaoyuzhou
- Keep key names exactly access_token/refresh_token as the normalizer expects
When it happens
Trigger: The JSON file exists but was hand-edited and a token field was deleted or left empty; normalizeXiaoyuzhouCredentials dropped the field because the key name didn't match; the file holds only one of the two tokens; fields are present but null/whitespace.
Common situations: Partial copy-paste of credentials during setup; exporting a token set that only contained access_token; a schema/key rename (e.g. refreshToken vs refresh_token) so normalization drops the value; truncated file write.
Related errors
- Failed to parse Xiaoyuzhou credential file: ${filePath}
- AUTH_REQUIRED
- creator.douyin.com
- 抖音上传授权缺少 AccessKeyID/SecretAccessKey/SessionToken
- delayMaxMs
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/c69d2d10b057a1a5.
Report an issue: GitHub.