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

  1. Open filePath and confirm both access_token and refresh_token keys exist with non-empty string values
  2. Re-create the file with a complete credential export from the xiaoyuzhou source (both tokens)
  3. Check key naming/casing matches what normalizeXiaoyuzhouCredentials expects (refresh_token vs refreshToken)
  4. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/c69d2d10b057a1a5. Report an issue: GitHub.