jackwener/OpenCLI · error · ArgumentError
status "${status}" not in {${TASK_STATUSES.join('|')}} — pre
Error message
status "${status}" not in {${TASK_STATUSES.join('|')}} — pre-network reject (saves a 400 round-trip). What it means
task-status validates the target status against TASK_STATUSES before touching the network, throwing an ArgumentError with the allowed set and an explicit note that this is a pre-network reject to save a 400 round-trip. The PATCH request is only issued once the status token is known-valid.
Source
Thrown at clis/slock/task-status.js:41
access: 'write',
description: `Set a task's status (PATCH /tasks/:taskId/status, body {status}). One of ${TASK_STATUSES.join('|')}.`,
domain: SLOCK_DOMAIN,
strategy: Strategy.COOKIE,
browser: true,
siteSession: 'persistent',
args: [
{ name: 'taskId', positional: true, required: true, help: 'Full task UUID (= message id; short ids rejected)' },
{ name: 'status', positional: true, required: true, help: `One of: ${TASK_STATUSES.join('|')}` },
{ name: 'server', help: 'Override active server' },
],
columns: ['taskId', 'taskStatus', 'assigneeId', 'taskNumber'],
func: async (page, kwargs) => {
let id;
try { id = assertMessageIdShape(String(kwargs.taskId ?? '')); }
catch (e) { throw new ArgumentError(e.message); }
const status = String(kwargs.status ?? '').trim();
if (!TASK_STATUSES.includes(status)) {
throw new ArgumentError(`status "${status}" not in {${TASK_STATUSES.join('|')}} — pre-network reject (saves a 400 round-trip).`);
}
await page.goto(SLOCK_HOME_URL);
const snippet = `
${authHeadersFragment({ serverScoped: true, serverIdOverride: kwargs.server })}
const res = await fetch('${SLOCK_API_BASE}/tasks/' + encodeURIComponent(${JSON.stringify(id)}) + '/status', {
method:'PATCH', credentials:'include', headers,
body: JSON.stringify({ status: ${JSON.stringify(status)} }),
});
if (res.status === 400) {
const j = await res.json().catch(() => ({}));
return { kind: 'http', status: 400, where: '/tasks/:taskId/status (bad request: ' + (j.error || j.message || 'invalid status transition') + ')' };
}
if (res.status === 403) return { kind: 'http', status: 403, where: '/tasks/:taskId/status (forbidden — terminal status (done/closed), not the assignee, or channel archived)' };
if (res.status === 404) return { kind: 'http', status: 404, where: '/tasks/:taskId/status (task not found)' };
// F6 — actionable hint for repeat-set 409 ("status already X").
if (res.status === 409) return { kind: 'http', status: 409, where: '/tasks/:taskId/status (conflict — task is already in status ' + ${JSON.stringify(status)} + '; no-op set rejected)' };
if (!res.ok) return { kind: res.status===401?'auth':'http', status: res.status, where:'/tasks/:taskId/status' };
const data = await res.json().catch(() => ({}));View on GitHub (pinned to 49907e53dc)
Solutions
- Run `slock task-status --help` and pass exactly one of the listed statuses
- Normalize casing/spelling to the canonical tokens (e.g. 'done' → whatever TASK_STATUSES defines)
- Validate the value in your wrapper before invoking to give users a friendlier message
- Present a picker/autocomplete of TASK_STATUSES instead of free-text input
Example fix
// before
await cli('slock', 'task-status', '--taskId', id, '--status', 'Done');
// after
await cli('slock', 'task-status', '--taskId', id, '--status', 'done'); // exact member Defensive patterns
Strategy: validation
Validate before calling
import { TASK_STATUSES } from 'clis/slock/task-statuses.js';
const status = String(rawStatus ?? '').trim();
if (!TASK_STATUSES.includes(status)) {
throw new Error(`status must be one of: ${TASK_STATUSES.join('|')}`);
} Type guard
const isValidStatus = (s) => TASK_STATUSES.includes(String(s ?? '').trim());
Prevention
- Only pass statuses listed in --help
- Offer a fixed-choice picker instead of free text in tooling
- Normalize casing/spelling to canonical tokens before invoking
When it happens
Trigger: Running `slock task-status --taskId <id> --status <value>` where value (trimmed) is not an exact TASK_STATUSES member — bad casing, aliases like 'wip'/'done', or an empty/unset variable producing ''.
Common situations: Typing shorthand statuses; scripts interpolating free-form user input; migration from another tracker's vocabulary; casing changes after the status set was renamed.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- ${label} must be <= ${maxValue}
- ${label} must be a positive integer
- ${label} must be >= ${min}
- indeed fromage must be one of 1/3/7/14 (days), got "${value}
- indeed sort must be "relevance" or "date", got "${value}"
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/8f0f33e1a6c4af25.
Report an issue: GitHub.