jackwener/OpenCLI · error · ArgumentError

Youdao Note URL must be under note.youdao.com or note.youdao

Error message

Youdao Note URL must be under note.youdao.com or note.youdao.cn

What it means

ArgumentError thrown by normalizeShareUrl when the URL hostname is not in ALLOWED_HOSTS (share.note.youdao.com, note.youdao.com, share.note.youdao.cn, note.youdao.cn). The extractor is written against Youdao's specific page structure, so arbitrary hosts would fail; the library rejects them up front.

Source

Thrown at clis/youdao/note.js:33

  return payload;
}

function normalizeShareUrl(raw) {
  const value = String(raw ?? '').trim();
  if (!value) {
    throw new ArgumentError('youdao note url cannot be empty', 'Pass a full public share URL from Youdao Notes.');
  }
  let parsed;
  try {
    parsed = new URL(value);
  } catch {
    throw new ArgumentError('Invalid Youdao Note URL', 'Example: https://share.note.youdao.com/ynoteshare/index.html?id=...&type=note');
  }
  if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
    throw new ArgumentError('Youdao Note URL must use http or https');
  }
  if (!ALLOWED_HOSTS.has(parsed.hostname)) {
    throw new ArgumentError('Youdao Note URL must be under note.youdao.com or note.youdao.cn');
  }
  if (!parsed.searchParams.get('id')) {
    throw new ArgumentError('Youdao Note URL must include an id query parameter');
  }
  const type = parsed.searchParams.get('type');
  if (type && type !== 'note') {
    throw new ArgumentError('youdao note only accepts shared note URLs', 'Shared notebooks are not implemented yet.');
  }
  return parsed.toString();
}

function formatYoudaoTimestamp(value) {
  if (value == null || value === '') return '';
  const numeric = Number(value);
  if (!Number.isFinite(numeric) || numeric <= 0) return String(value);
  const millis = numeric < 10_000_000_000 ? numeric * 1000 : numeric;
  const date = new Date(millis);
  if (Number.isNaN(date.getTime())) return String(value);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Use a URL whose host is exactly share.note.youdao.com, note.youdao.com, share.note.youdao.cn, or note.youdao.cn
  2. Re-copy the share link via Youdao Notes' 'Share' feature, which always emits a share.note.youdao.com(/.cn) URL
  3. Fix typos in the hostname and drop any extra subdomains or suffixes

Example fix

// before
await youdaoNote('https://www.youdao.com/ynoteshare/index.html?id=abc');
// after
await youdaoNote('https://share.note.youdao.com/ynoteshare/index.html?id=abc');
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = new Set(['share.note.youdao.com','note.youdao.com','share.note.youdao.cn','note.youdao.cn']);
function isYoudaoShareHost(u) {
  try { return ALLOWED.has(new URL(u).hostname); } catch { return false; }
}
if (!isYoudaoShareHost(inputUrl)) throw new Error('URL must be a youdao note share link');

Type guard

function isYoudaoShareUrl(v) {
  const hosts = ['share.note.youdao.com','note.youdao.com','share.note.youdao.cn','note.youdao.cn'];
  try { return hosts.includes(new URL(v).hostname); } catch { return false; }
}

Try / catch

try {
  await youdaoNote(url);
} catch (e) {
  if (String(e.message).includes('note.youdao.com or note.youdao.cn')) {
    // re-copy the correct share.note.youdao.com link
  } else throw e;
}

Prevention

When it happens

Trigger: Passing URLs from other domains (e.g. 'www.youdao.com/...', 'youdao.com', 'you.163.com', 'example.com/index.html?id=...') or a subdomain not in the allowlist such as 'share.note.youdao.com.evil.io'.

Common situations: Confusing Youdao's main site with its note-sharing host; using a translated/proxied mirror of the share page; typos in the domain (e.g. 'notes.youdao.com'); passing an entirely different notes service URL.

Related errors


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