jackwener/OpenCLI · error · ArgumentError
attachmentId "${id}" is not a UUID
Error message
attachmentId "${id}" is not a UUID What it means
This ArgumentError is thrown by the attachment-url command when the attachmentId argument does not match the UUID regular expression. Attachment IDs are UUIDs issued by the server; anything else would produce a malformed API path, so the CLI validates the shape before navigating and running the fetch snippet.
Source
Thrown at clis/slock/attachment-url.js:33
import { UUID_RE } from './resolve.js';
cli({
site: SLOCK_SITE,
name: 'attachment-url',
access: 'read',
description: 'Get a short-lived signed CDN URL for an attachment (does not download bytes).',
domain: SLOCK_DOMAIN,
strategy: Strategy.COOKIE,
browser: true,
siteSession: 'persistent',
args: [
{ name: 'attachmentId', positional: true, required: true, help: 'Attachment UUID' },
{ name: 'server', help: 'Override active server slug' },
],
columns: ['attachmentId', 'url', 'expiresAt'],
func: async (page, kwargs) => {
const id = String(kwargs.attachmentId ?? '').trim();
if (!UUID_RE.test(id)) throw new ArgumentError(`attachmentId "${id}" is not a UUID`);
await page.goto(SLOCK_HOME_URL);
const snippet = buildFetchSnippet({
method: 'GET',
path: `/attachments/${encodeURIComponent(id)}/url`,
serverScoped: true,
serverIdOverride: kwargs.server,
});
const result = await page.evaluate(`(async () => { ${snippet} })()`);
const rows = dispatchEvaluateResult(result);
const data = Array.isArray(rows) ? rows[0] : rows;
if (!data?.url) {
throw new CommandExecutionError(`no signed url returned for attachment ${id}`);
}
return [{
attachmentId: id,
url: data?.url ?? null,
expiresAt: data?.expiresAt ?? null,
}];View on GitHub (pinned to 49907e53dc)
Solutions
- Run the attachment listing command to get the correct UUID-form attachmentId and pass that.
- Strip any URL/path prefix so only the bare UUID remains (e.g. extract the last path segment).
- Validate the format first: /^[0-9a-f]{8}-[0-9a-f]{4}-.../i (standard 8-4-4-4-12 hex pattern) before invoking.
- Confirm the attachment exists on the target server; ids from other servers won't resolve.
Example fix
// before
await attachmentUrl(page, process.argv[2]); // "https://slock.example/attachments/abc"
// after
const id = process.argv[2].split('/').pop();
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) throw new Error('not a UUID');
await attachmentUrl(page, id); Defensive patterns
Strategy: validation
Validate before calling
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!UUID_RE.test(attachmentId)) throw new Error(`attachmentId must be a UUID, got: ${attachmentId}`); Type guard
const isUuid = (v) => typeof v === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v); Try / catch
try {
await attachmentUrl(page, id);
} catch (e) {
if (e instanceof ArgumentError && e.message.includes('is not a UUID')) {
console.error(`Bad id "${id}" — re-list attachments to get the UUID`);
} else throw e;
} Prevention
- Always take attachmentId from the CLI/API listing output, never free text.
- Run ids through isUuid() at script boundaries.
- Strip URL prefixes before passing an id that came from a link.
When it happens
Trigger: Passing a non-UUID string as the positional attachmentId — e.g. a numeric database id, a URL, a filename, an empty string after trimming, or an ID from a different system.
Common situations: Copying the wrong column from a previous listing (e.g. server id instead of attachment id), pasting a URL fragment instead of the bare UUID, or using an id from another chat platform.
Related errors
- series_id must be a non-empty value
- Invalid Chess.com username "${value}" Usernames are 3-25 cha
- coingecko derivatives limit must be a positive integer
- limit must be a positive integer
- Search keyword cannot be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/0ef064cecd789166.
Report an issue: GitHub.