jackwener/OpenCLI · error · CommandExecutionError

Bilibili relation modify did not verify ${expectedLabel}; la

Error message

Bilibili relation modify did not verify ${expectedLabel}; last attribute=${lastAttribute}

What it means

waitForRelation polls Bilibili's relation API (attribute = your relationship flag with a user, e.g. follow/block states) after a follow/unfollow/block mutation, applying a predicate with a 5s deadline. If the relation attribute never satisfies the predicate within the timeout window (polling every 500ms), it throws CommandExecutionError with the last observed attribute so you can see what state the API actually reports. This means the modify command ran, but the change was not (yet) reflected server-side.

Source

Thrown at clis/bilibili/relation.js:41

    const payload = await fetchJson(page, `https://api.bilibili.com/x/relation?fid=${mid}`);
    requireOkPayload(payload, 'relation query');
    const attribute = payload?.data?.attribute;
    if (typeof attribute !== 'number') {
        throw new CommandExecutionError('Bilibili relation query returned a malformed attribute');
    }
    return attribute;
}

export async function waitForRelation(page, mid, predicate, expectedLabel) {
    const deadline = Date.now() + RELATION_VERIFY_TIMEOUT_MS;
    let lastAttribute;
    while (Date.now() <= deadline) {
        lastAttribute = await fetchRelationAttribute(page, mid);
        if (predicate(lastAttribute)) return lastAttribute;
        if (typeof page.wait !== 'function') break;
        await page.wait({ time: RELATION_VERIFY_POLL_MS / 1000 });
    }
    throw new CommandExecutionError(
        `Bilibili relation modify did not verify ${expectedLabel}; last attribute=${lastAttribute}`,
    );
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command after a few seconds — often the attribute eventually flips; server lag is the most common cause
  2. Check the lastAttribute value in the message against Bilibili's relation attribute enum to see what state you are actually in (e.g. already-followed vs not-followed)
  3. Verify the mid is correct and the account is not risk-controlled/limited (try the same action manually in a browser)
  4. Ensure the automation environment provides a real browser page with a wait() method so all polls within the 5s window run
  5. If operations legitimately take longer, increase RELATION_VERIFY_TIMEOUT_MS or wrap the command in an application-level retry with backoff

Example fix

// before
await waitForRelation(page, mid, (a) => a === 2, 'followed');
// after
for (let attempt = 1; attempt <= 3; attempt++) {
  try { await waitForRelation(page, mid, (a) => a === 2, 'followed'); break; }
  catch (e) { if (attempt === 3) throw e; await new Promise(r => setTimeout(r, 2000)); }
}
Defensive patterns

Strategy: retry

Validate before calling

const attr = await fetchRelationAttribute(page, mid);
if (!predicate(attr)) console.warn('relation not yet in expected state:', attr);

Try / catch

try {
  await waitForRelation(page, mid, (a) => a === 2, 'followed');
} catch (e) {
  if (String(e.message).includes('did not verify')) {
    // inspect lastAttribute, back off and retry the whole modify+verify cycle
  } else throw e;
}

Prevention

When it happens

Trigger: Calling a bilibili relation command (follow/unfollow/block) and then the relation API keeps returning an attribute that fails the predicate for 5+ seconds; also triggered when page.wait is not a function — the loop breaks after a single poll and throws immediately even though the server may just need more time.

Common situations: Bilibili server-side lag/replication delay after a follow; the mutation silently failed (invalid mid, risk-control shadow rejection, need 2FA for some operations); an already-followed user being unfollowed but attribute mapping differs; running in an environment where the page object lacks wait() so only one poll happens.

Related errors


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