pbakaus/impeccable · error
No live copy-edit AI runner is available.
Error message
No live copy-edit AI runner is available.
What it means
Thrown by runCopyEditBatchAgent() when chooseCopyEditAgent() returns null — no mock/chat/codex/claude provider resolved. The provider is chosen from env.IMPECCABLE_LIVE_COPY_AGENT (default 'auto') combined with auth/availability checks (commandAuthed for codex/claude, chatAvailable() for chat). The message body comes from describeNoProviderError(), which lists per-provider status. This fires before any prompt is built.
Source
Thrown at plugin/skills/impeccable/scripts/live-copy-edit-agent.mjs:114
export async function runCopyEditBatchAgent(batch, opts = {}) {
const cwd = opts.cwd || process.cwd();
const env = opts.env || process.env;
const provider = opts.provider || chooseCopyEditAgent({ env, chatAvailable: opts.chatAvailable });
if (provider === 'mock') {
const delayMs = Number(env.IMPECCABLE_LIVE_COPY_AGENT_MOCK_DELAY_MS || 0);
if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
return mockBatchResult(batch, env, cwd);
}
if (provider === 'chat') {
if (typeof opts.applyBatchToSource !== 'function') {
throw new Error('chat provider requires applyBatchToSource callback');
}
const raw = await opts.applyBatchToSource(batch, { repair: batch?.repair || null });
return normalizeBatchResult(raw || {});
}
if (!provider) {
throw new Error(describeNoProviderError({ env }));
}
const prompt = buildCopyEditBatchPrompt(batch, { cwd });
const outDir = opts.outDir || fs.mkdtempSync(path.join(os.tmpdir(), 'impeccable-copy-batch-'));
fs.mkdirSync(outDir, { recursive: true });
const resultPath = path.join(outDir, 'result.json');
const logPath = path.join(outDir, 'agent.log');
if (provider === 'codex') {
await runCodex(prompt, { cwd, env, resultPath, logPath, timeoutMs: opts.timeoutMs });
} else if (provider === 'claude') {
await runClaude(prompt, { cwd, env, resultPath, logPath, timeoutMs: opts.timeoutMs });
} else {
throw new Error(`Unsupported live copy-edit AI runner: ${provider}`);
}
const output = fs.existsSync(resultPath) ? fs.readFileSync(resultPath, 'utf-8') : '';
const parsed = parseCopyEditBatchResult(output);View on GitHub (pinned to d14711ae3d)
Solutions
- Install and authenticate a runner: `codex login` or `claude setup-token` then export CLAUDE_CODE_OAUTH_TOKEN (or ANTHROPIC_API_KEY) in the shell that starts live-server.mjs.
- Start an Impeccable live chat session so chatAvailable() returns true and route Apply through the chat agent.
- Set IMPECCABLE_LIVE_COPY_AGENT=mock for local/test runs that don't need a real model.
- Read the full describeNoProviderError() output — it itemises which provider is missing/unauthed, so fix the first applicable bullet.
Example fix
// before (no runner available)
await runCopyEditBatchAgent(batch, { cwd });
// after
// shell: export CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
// or: codex login
// or: IMPECCABLE_LIVE_COPY_AGENT=mock for tests
// programmatic guard:
import { chooseCopyEditAgent } from './live-copy-edit-agent.mjs';
if (!chooseCopyEditAgent({ env: process.env })) {
throw new Error('Enable a copy-edit runner before Apply.');
}
await runCopyEditBatchAgent(batch, { cwd }); Defensive patterns
Strategy: validation
Validate before calling
import { chooseCopyEditAgent } from './live-copy-edit-agent.mjs';
const provider = chooseCopyEditAgent({ env: process.env });
if (!provider) {
// describeNoProviderError() itemises what's missing;
// either install/auth a runner, start a chat session, or set mock.
throw new Error('No copy-edit runner. Set IMPECCABLE_LIVE_COPY_AGENT=mock for tests.');
}
await runCopyEditBatchAgent(batch, { cwd }); Try / catch
try {
await runCopyEditBatchAgent(batch, { cwd });
} catch (err) {
if (/No live copy-edit AI runner/.test(err.message)) {
// err.message is the full describeNoProviderError() breakdown — surface it
console.error(err.message);
process.exit(1);
}
throw err;
} Prevention
- Start live-server.mjs from a shell that has CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY exported — headless subprocesses can't read the macOS Keychain.
- For CI/tests, set IMPECCABLE_LIVE_COPY_AGENT=mock so the provider is always resolvable.
- Run `codex login` / `claude setup-token` once per environment; re-run after token rotation.
When it happens
Trigger: IMPECCABLE_LIVE_COPY_AGENT unset/auto AND neither codex nor claude passes commandAuthed AND no live chat session is polling; explicitly set to 'off'/'none'/'false'/'0'; set to 'chat' but chatAvailable() is false; set to 'codex'/'claude' but the CLI binary is not on PATH. Also when an unrecognised mode string is set (auto-falls through to null at line 559).
Common situations: Fresh machine without codex/claude CLI installed; CLAUDE_CODE_OAUTH_TOKEN expired so claude is no longer authed; claude installed but macOS Keychain unreachable from a no-TTY subprocess (the comment at line 591 explains this); dev server started in a shell that lacks the API key env vars; user disabled the agent with IMPECCABLE_LIVE_COPY_AGENT=none.
Related errors
- Unsupported live copy-edit AI runner: ${provider}
- AI copy-edit batch did not return a valid completion payload
- concept-seed: no local catalog (set IMPECCABLE_CATALOG_DIR o
- No live copy-edit AI runner is available.
- Unsupported live copy-edit AI runner: ${provider}
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/b792eec46181ac44.
Report an issue: GitHub.