{"record":{"id":"17c5df5f4138059a","repo":"jackwener/OpenCLI","slug":"no-signed-url-returned-for-attachment-id-17c5df","errorCode":null,"errorMessage":"no signed url returned for attachment ${id}","messagePattern":"no signed url returned for attachment (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/slock/attachment-url.js","lineNumber":45,"sourceCode":"    { name: 'attachmentId', positional: true, required: true, help: 'Attachment UUID' },\n    { name: 'server', help: 'Override active server slug' },\n  ],\n  columns: ['attachmentId', 'url', 'expiresAt'],\n  func: async (page, kwargs) => {\n    const id = String(kwargs.attachmentId ?? '').trim();\n    if (!UUID_RE.test(id)) throw new ArgumentError(`attachmentId \"${id}\" is not a UUID`);\n    await page.goto(SLOCK_HOME_URL);\n    const snippet = buildFetchSnippet({\n      method: 'GET',\n      path: `/attachments/${encodeURIComponent(id)}/url`,\n      serverScoped: true,\n      serverIdOverride: kwargs.server,\n    });\n    const result = await page.evaluate(`(async () => { ${snippet} })()`);\n    const rows = dispatchEvaluateResult(result);\n    const data = Array.isArray(rows) ? rows[0] : rows;\n    if (!data?.url) {\n      throw new CommandExecutionError(`no signed url returned for attachment ${id}`);\n    }\n    return [{\n      attachmentId: id,\n      url: data?.url ?? null,\n      expiresAt: data?.expiresAt ?? null,\n    }];\n  },\n});\n","sourceCodeStart":27,"sourceCodeEnd":54,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/slock/attachment-url.js#L27-L54","documentation":"This CommandExecutionError is thrown after the in-page fetch for GET /attachments/:id/url completes successfully but the returned payload contains no `url` field. It indicates the server responded without the signed URL the CLI contract expects — either contract drift, an unexpected response shape, or a deleted/expired attachment.","triggerScenarios":"The dispatchEvaluateResult call returns an object whose `data.url` is undefined/null: server returns an empty object, a row missing `url`, or a null envelope for a nonexistent attachment.","commonSituations":"Requesting a URL for an attachment that was deleted or whose record lacks a stored file, an API version change renaming the `url` field, or the session lacking permission to mint signed URLs (server returns 200 with empty body).","solutions":["Re-list attachments to confirm the attachmentId still exists and is accessible.","Check the Slock server API version; if `/attachments/:id/url` changed shape, update the CLI.","Retry after re-authenticating — verify the session with auth-verify in case permissions silently degraded.","If it persists, inspect the raw response (the error implies contract drift) and report/patch dispatchEvaluateResult mapping."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const hasSignedUrl = (data) => data != null && typeof data === 'object' && typeof data.url === 'string' && data.url.length > 0;","tryCatchPattern":"try {\n  const [row] = await attachmentUrl(page, id);\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.includes('no signed url returned')) {\n    console.error(`Attachment ${id} has no signed URL — verify it still exists`);\n  } else throw e;\n}","preventionTips":["Confirm the attachment exists via listing before requesting its URL.","Re-verify the session when signed URLs come back empty.","Treat missing url as 'attachment unavailable', not a transient failure."],"tags":["network","api-contract","signed-url","response-shape"],"backgroundTag":"missing-field-in-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}