{"record":{"id":"60b09f90ed2a87f1","repo":"slopus/happy","slug":"request-upload-returned-an-invalid-response","errorCode":null,"errorMessage":"request-upload returned an invalid response","messagePattern":"request-upload returned an invalid response","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/api/apiSession.ts","lineNumber":429,"sourceCode":"\n    private async requestAttachmentUpload(filename: string, size: number): Promise<AttachmentUploadResult> {\n        const response = await axios.post<AttachmentUploadResult>(\n            `${configuration.serverUrl}/v1/sessions/${encodeURIComponent(this.sessionId)}/attachments/request-upload`,\n            { filename, size },\n            {\n                headers: this.authHeaders(),\n                timeout: 30000,\n            },\n        );\n\n        const upload = response.data;\n        if (\n            !upload\n            || typeof upload.ref !== 'string'\n            || typeof upload.uploadUrl !== 'string'\n            || (upload.method !== undefined && upload.method !== 'PUT' && upload.method !== 'POST')\n        ) {\n            throw new Error('request-upload returned an invalid response');\n        }\n\n        return {\n            ...upload,\n            method: upload.method ?? 'PUT',\n        };\n    }\n\n    private async uploadEncryptedAttachmentBlob(upload: AttachmentUploadResult, encrypted: Uint8Array): Promise<void> {\n        if (upload.method === 'POST') {\n            const { body, boundary } = buildMultipartUploadBody(upload.formFields, encrypted);\n            await axios.post(upload.uploadUrl, body, {\n                headers: {\n                    'Content-Type': `multipart/form-data; boundary=${boundary}`,\n                },\n                timeout: 60000,\n                maxBodyLength: 10 * 1024 * 1024,\n            });","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/api/apiSession.ts#L411-L447","documentation":"Thrown by requestAttachmentUpload when the server's response to the request-upload call fails structural validation: upload is missing, ref/uploadUrl are not strings, or method is present but not PUT or POST. The client treats this as a contract violation of the upload API rather than a transport error.","triggerScenarios":"Server returns an error body, empty data, or a malformed payload for request-upload; an API version skew changes the response shape (renamed fields, missing uploadUrl, unsupported method value).","commonSituations":"Pointing the CLI at an older/proxy server that doesn't implement request-upload; authenticated but unauthorized requests returning error JSON; server bug or partial outage.","solutions":["Check server logs and the raw response for the real cause (auth failure, 4xx/5xx body)","Verify CLI and server versions are compatible — update the CLI or server","Confirm the attachment upload endpoint is enabled/reachable and retry"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const res = await requestUpload(attachmentId);\nconst ok = res?.data?.ref && typeof res.data.uploadUrl === 'string'\n  && ['PUT', 'POST', undefined].includes(res.data.method);\nif (!ok) throw new Error('unexpected request-upload response');","typeGuard":"function isValidUploadResponse(u: any): u is { ref: string; uploadUrl: string; method?: 'PUT' | 'POST' } {\n  return !!u && typeof u.ref === 'string' && typeof u.uploadUrl === 'string'\n    && (u.method === undefined || u.method === 'PUT' || u.method === 'POST');\n}","tryCatchPattern":"try {\n  await upload(attachmentPath);\n} catch (e) {\n  if (e.message.includes('invalid response')) {\n    // log raw server response; likely version skew or auth issue\n  } throw e;\n}","preventionTips":["Pin matching CLI/server versions","Log raw request-upload responses when debugging","Check auth status before attachment operations"],"tags":["api-contract","upload","response-validation","server"],"backgroundTag":"invalid-api-response","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}