jackwener/OpenCLI · error · CommandExecutionError

Slock thread-follow succeeded without returning a thread…

Error message

Slock thread-follow succeeded without returning a thread channel id for parent message ${id}.

What it means

After following a thread, thread-follow reads `threadChannelId ?? channelId ?? id` from the API response; if none is present it throws this CommandExecutionError because the whole point of the command is to report the created/found thread channel id. Success without an identifiable channel id is treated as contract drift.

Solutions

  1. Log the raw evaluate result to see what the follow endpoint actually returned.
  2. Handle the already-following case separately — look the channel id up via thread-list instead.
  3. Update the CLI/server to matching versions so the response includes threadChannelId.
  4. If the field is nested, unwrap it in dispatchEvaluateResult before the check.

Example fix

// before
const data = dispatchEvaluateResult(result);
const threadChannelId = data?.threadChannelId ?? data?.channelId ?? data?.id;
// after
const raw = dispatchEvaluateResult(result);
const data = raw?.thread ?? raw;
const threadChannelId = data?.threadChannelId ?? data?.channelId ?? data?.id;
Defensive patterns

Strategy: fallback

Type guard

function hasThreadChannelId(d) { return !!(d?.threadChannelId ?? d?.channelId ?? d?.id); }

Try / catch

try {
  const r = await cli.run(['thread-follow', '--parent-message-id', id]);
} catch (e) {
  if (String(e.message).includes('without returning a thread channel id')) {
    // likely already-following: look it up
    const threads = await cli.run(['thread-list']);
    const hit = threads.find(t => t.parentMessageId === id);
  } else throw e;
}

Prevention

When it happens

Trigger: POST /channels/threads/follow returns 2xx but the body lacks threadChannelId/channelId/id — e.g. the endpoint returns {kind:'ok'} with no payload, an already-following response with an empty body, or a renamed field in a newer API version.

Common situations: Following a thread you already follow (API short-circuits with no payload); server version older/newer than the CLI expects; proxies stripping response bodies.

Related errors


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

Appendix: source

Thrown at clis/slock/thread-follow.js:42

  ],
  columns: ['parentMessageId', 'threadChannelId', 'result'],
  func: async (page, kwargs) => {
    let id;
    try { id = assertMessageIdShape(String(kwargs.parentMessageId ?? '')); }
    catch (e) { throw new ArgumentError(e.message); }
    await page.goto(SLOCK_HOME_URL);
    const snippet = buildFetchSnippet({
      method: 'POST',
      path: '/channels/threads/follow',
      body: { parentMessageId: id },
      serverScoped: true,
      serverIdOverride: kwargs.server,
    });
    const result = await page.evaluate(`(async () => { ${snippet} })()`);
    const data = dispatchEvaluateResult(result);
    const threadChannelId = data?.threadChannelId ?? data?.channelId ?? data?.id;
    if (!threadChannelId) {
      throw new CommandExecutionError(`Slock thread-follow succeeded without returning a thread channel id for parent message ${id}.`);
    }
    return [{ parentMessageId: id, threadChannelId, result: 'followed' }];
  },
});

View on GitHub (pinned to 49907e53dc)