{"record":{"id":"6980d63491cb8453","repo":"jackwener/OpenCLI","slug":"quark-action-resp-message","errorCode":null,"errorMessage":"quark: ${action}: ${resp.message}","messagePattern":"quark: (.+?): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/quark/utils.js","lineNumber":24,"sourceCode":"const AUTH_HINT = 'Quark Drive requires a logged-in browser session';\nfunction isAuthFailure(message, status) {\n    if (status === 401 || status === 403)\n        return true;\n    return /not logged in|login required|please log in|authentication required|unauthorized|forbidden|未登录|请先登录|需要登录|登录/.test(message.toLowerCase());\n}\nfunction getErrorStatus(error) {\n    if (!error || typeof error !== 'object' || !('status' in error))\n        return undefined;\n    const status = error.status;\n    return typeof status === 'number' ? status : undefined;\n}\nfunction unwrapApiData(resp, action) {\n    if (resp.status === 200)\n        return resp.data;\n    if (isAuthFailure(resp.message, resp.status)) {\n        throw new AuthRequiredError(QUARK_DOMAIN, AUTH_HINT);\n    }\n    throw new CommandExecutionError(`quark: ${action}: ${resp.message}`);\n}\nexport function extractPwdId(url) {\n    const m = url.match(/\\/s\\/([a-zA-Z0-9]+)/);\n    if (m)\n        return m[1];\n    if (/^[a-zA-Z0-9]+$/.test(url))\n        return url;\n    throw new ArgumentError(`Invalid Quark share URL: ${url}`);\n}\nexport async function fetchJson(page, url, options) {\n    const method = options?.method || 'GET';\n    const body = options?.body ? JSON.stringify(options.body) : undefined;\n    const js = `fetch(${JSON.stringify(url)}, {\n    method: ${JSON.stringify(method)},\n    headers: { 'Content-Type': 'application/json' },\n    credentials: 'include',\n    ${body ? `body: ${JSON.stringify(body)},` : ''}\n  }).then(async r => {","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/quark/utils.js#L6-L42","documentation":"unwrapApiData throws CommandExecutionError(`quark: ${action}: ${resp.status/message}`) when the Quark API returns a non-200 business status that is NOT recognized as an auth failure. The action label (e.g. the API operation name) and the API's own message are surfaced so the developer can see exactly which call failed and why, per Quark's resp.status/resp.message envelope.","triggerScenarios":"apiPost/apiGet responses with business error statuses: deleting/moving nonexistent fids, renaming to an invalid or duplicate name, saving a share that no longer exists, insufficient storage quota, rate limiting, or any other Quark-declared error.","commonSituations":"Stale fids from cached listings (files already deleted); renaming a file to a name that already exists; saving an expired share link; hitting Quark rate limits during bulk scripts; Quark API message format changes.","solutions":["Read the embedded resp.message — it is Quark's own error text and names the concrete cause.","Refresh fids via a fresh listing before mutating operations; stale ids are a common cause.","Check storage quota and share-link validity for save operations.","Add delays/backoff between bulk API calls if rate limiting is indicated.","If messages look truncated or generic, verify unwrapApiData still matches Quark's current response envelope."],"exampleFix":"// before: ignoring API business status\nawait apiPost(page, url, payload);\n// after: inspect the returned result for status info\nconst result = await apiPost(page, url, payload);\nif (result && result.status && result.status !== 200) {\n  console.error('quark api said:', result.message);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isQuarkApiError(e) {\n  return e instanceof CommandExecutionError &&\n    /^quark: /.test(e.message);\n}","tryCatchPattern":"try {\n  await apiPost(page, url, payload);\n} catch (e) {\n  if (isQuarkApiError(e)) {\n    const apiMsg = e.message.replace(/^quark: [^:]+: /, '');\n    console.error('Quark rejected the request:', apiMsg);\n    // branch on known messages: stale fid / quota / rate limit\n  } else throw e;\n}","preventionTips":["Refresh fids from listings immediately before mutations.","Respect rate limits: add delays/backoff in bulk scripts.","Check quota and share validity before save operations.","Log the embedded resp.message verbatim for diagnosis."],"tags":["api-error","quark","business-logic"],"backgroundTag":"api-business-error","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}