jackwener/OpenCLI · error · CommandExecutionError
douyin hashtag search: API returned suggestions but none had
Error message
douyin hashtag search: API returned suggestions but none had a stable shape
What it means
Thrown by the douyin hashtag CLI 'search' action when the creator.douyin.com suggestion API returned data, but none of the suggestion entries could be normalized into the expected row shape ({name, id, view_count}). The library treats an all-unusable suggestion list as a failure rather than silently returning empty results.
Source
Thrown at clis/douyin/hashtag.js:77
const action = kwargs.action;
if (action === 'search') {
const keyword = String(kwargs.keyword ?? '').trim();
// challenge/search answers 200 with an empty body; the creator
// studio composer reads suggestions from this endpoint instead,
// which ignores count and returns a fixed-size list (#2205).
const url = `https://creator.douyin.com/aweme/v1/search/challengesug/?keyword=${encodeURIComponent(keyword)}&source=challenge_create&aid=2906`;
const res = await browserFetch(page, 'GET', url);
const list = requireListField(res, 'sug_list', 'search');
const rows = list.flatMap(c => {
if (!isPlainObject(c) || typeof c.cha_name !== 'string' || !c.cha_name) return [];
return [{
name: c.cha_name,
id: c.cid ?? '',
view_count: c.view_count ?? 0,
}];
});
if (list.length > 0 && rows.length === 0) {
throw new CommandExecutionError('douyin hashtag search: API returned suggestions but none had a stable shape');
}
return rows.slice(0, kwargs.limit);
}
if (action === 'suggest') {
const cover = String(kwargs.cover ?? '').trim();
const url = `https://creator.douyin.com/web/api/media/hashtag/rec/?cover_uri=${encodeURIComponent(cover)}&aid=1128`;
const res = await browserFetch(page, 'GET', url);
const list = requireListField(res, 'hashtag_list', 'suggest');
return list.map(h => ({ name: h?.name ?? '', id: h?.id ?? '', view_count: h?.view_count ?? 0 }));
}
if (action === 'hot') {
const kw = String(kwargs.keyword ?? '').trim();
const url = `https://creator.douyin.com/aweme/v1/hotspot/recommend/?${kw ? `keyword=${encodeURIComponent(kw)}&` : ''}aid=1128`;
const res = await browserFetch(page, 'GET', url);
if (!isPlainObject(res)) {
throw new CommandExecutionError('douyin hashtag hot: API returned malformed payload');
}
const hotspotList = res.hotspot_list;View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run the command; transient payloads (captcha/anti-bot interstitials) often resolve on retry with a warm logged-in session.
- Verify the browser session is logged in to creator.douyin.com (cookies fresh, not expired).
- Check for a library update — Douyin frequently changes the response schema; a newer version may map the new fields.
- Inspect the raw API response manually (same URL in the logged-in browser) to confirm the schema and file an issue if it changed.
- Fall back to the 'hot' action or a different keyword if search suggestions are unavailable for the term.
Example fix
// before: assuming every suggestion has usable fields
const rows = list.map(c => [{ name: c.cha_name, id: c.cid ?? '', view_count: c.view_count ?? 0 }]);
// after: log the unexpected shape before throwing, to aid diagnosis
if (list.length > 0 && rows.length === 0) {
console.error('raw suggestions:', JSON.stringify(list).slice(0, 500));
throw new CommandExecutionError('douyin hashtag search: API returned suggestions but none had a stable shape');
} Defensive patterns
Strategy: type-guard
Validate before calling
// filter suggestions to those with a usable shape before calling
const usable = raw.filter(c => c && typeof c.cha_name === 'string' && c.cid != null);
if (usable.length === 0) throw new Error('no well-formed douyin hashtag suggestions for this keyword'); Type guard
function isShapedSuggestion(c) {
return c != null && typeof c === 'object' && typeof c.cha_name === 'string' && (typeof c.cid === 'string' || typeof c.cid === 'number');
} Try / catch
try {
const rows = await hashtag({ action: 'search', keyword: kw, limit: 10 });
} catch (e) {
if (String(e.message).includes('none had a stable shape')) {
// fall back to hot list or surface a schema-change warning
console.warn('douyin suggestion schema changed; falling back');
} else throw e;
} Prevention
- Keep the library updated — Douyin changes response schemas often.
- Check login state before calling search.
- Log raw suggestion payloads when the error occurs to detect schema drift early.
- Retry once before treating it as a hard failure.
When it happens
Trigger: The suggestion endpoint responds with JSON where the candidate array exists but every entry lacks recognizable fields (e.g. cha_name/cid), or rows end up empty while the raw list is non-empty, so `list.length > 0 && rows.length === 0` becomes true at clis/douyin/hashtag.js:77.
Common situations: Douyin changed the suggestion response schema (renamed cha_name/cid); the endpoint returned an HTML login page or captcha JSON instead of suggestions; the API is behind an A/B test returning a new shape.
Related errors
- TOS init response missing UploadId: ${res.body}
- douyin hashtag hot: API returned malformed payload
- douyin hashtag hot: API returned malformed "hotspot_list"
- douyin hashtag hot: API returned malformed "all_sentences"
- archive search returned malformed payload: result row is mis
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/2544b432f57f97cd.
Report an issue: GitHub.