{"record":{"id":"6dae3712b5253022","repo":"twentyhq/twenty","slug":"completefileupload-mutation-did-not-return-a-file","errorCode":null,"errorMessage":"completeFileUpload mutation did not return a file id","messagePattern":"completeFileUpload mutation did not return a file id","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-media.util.ts","lineNumber":356,"sourceCode":"};\n\nconst completeFileUpload = async ({\n  metadataClient,\n  fileId,\n}: {\n  metadataClient: InstanceType<typeof MetadataApiClient>;\n  fileId: string;\n}): Promise<string> => {\n  const mutationResult = await metadataClient.mutation({\n    completeFileUpload: {\n      __args: { fileId },\n      id: true,\n    },\n  });\n  const uploadedFileId = mutationResult.completeFileUpload?.id;\n\n  if (isUndefined(uploadedFileId)) {\n    throw new Error('completeFileUpload mutation did not return a file id');\n  }\n\n  return uploadedFileId;\n};\n\nconst parseContentLengthBytes = (\n  headerValue: string | null,\n): number | undefined => {\n  if (!isNonEmptyString(headerValue)) {\n    return undefined;\n  }\n\n  const parsedBytes = Number(headerValue.trim());\n\n  return Number.isFinite(parsedBytes) && parsedBytes >= 0\n    ? parsedBytes\n    : undefined;\n};","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-media.util.ts#L338-L374","documentation":"After the media bytes are uploaded to the presigned URL, the flow calls the metadata `completeFileUpload` mutation with the `fileId` to finalize the upload and receive the canonical file id. If `mutationResult.completeFileUpload?.id` is undefined, finalization did not return an id and the flow throws. This typically means the upload never completed server-side or the fileId is unknown to the server.","triggerScenarios":"The PUT to the presigned `uploadUrl` failed or was incomplete so the server has no finalized file; the `fileId` passed to `completeFileUpload` does not match the one returned by `createFileUpload`; the server rejected finalization (quota, virus scan, size) and returned a null/error shape.","commonSituations":"A network blip during the PUT leaving the multipart upload incomplete; passing a stale `fileId` from a previous attempt; a server-side post-upload validation rejecting the file.","solutions":["Confirm the upstream PUT to `uploadUrl` succeeded (capture its status) before calling `completeFileUpload`.","Ensure the `fileId` passed in is exactly the one returned by the preceding `createFileUpload` call in this same flow.","Log `mutationResult` in full (including `errors`) to surface the server's reason for omitting the id.","If finalization is transiently failing, retry the `completeFileUpload` mutation with backoff before aborting the whole import."],"exampleFix":"// before\nconst uploadedFileId = mutationResult.completeFileUpload?.id;\nif (isUndefined(uploadedFileId)) {\n  throw new Error('completeFileUpload mutation did not return a file id');\n}\n\n// after — include the fileId and any server errors in the message\nconst uploadedFileId = mutationResult.completeFileUpload?.id;\nif (isUndefined(uploadedFileId)) {\n  throw new Error(\n    `completeFileUpload mutation did not return a file id for fileId=${fileId}; server errors: ${JSON.stringify(mutationResult.errors ?? [])}`,\n  );\n}","handlingStrategy":"validation","validationCode":"// Confirm the PUT to the presigned URL succeeded before completing.\nif (!uploadOk) {\n  throw new Error(`Cannot completeFileUpload for ${fileId}: upstream PUT did not succeed`);\n}","typeGuard":"const hasCompletedFileId = (r: unknown): r is { completeFileUpload: { id: string } } =>\n  typeof r === 'object' &&\n  r !== null &&\n  typeof (r as any).completeFileUpload?.id === 'string';","tryCatchPattern":"async function completeWithRetry(metadataClient: any, fileId: string, attempts = 3): Promise<string> {\n  let lastErr: unknown;\n  for (let i = 0; i < attempts; i++) {\n    try {\n      const r = await metadataClient.mutation({ completeFileUpload: { __args: { fileId }, id: true } });\n      if (typeof r.completeFileUpload?.id === 'string') return r.completeFileUpload.id;\n      lastErr = new Error('completeFileUpload returned no id');\n    } catch (e) { lastErr = e; }\n    await new Promise((res) => setTimeout(res, 2 ** i * 400));\n  }\n  throw lastErr;\n}","preventionTips":["Verify the upstream PUT to the presigned URL succeeded before calling completeFileUpload.","Pass exactly the fileId returned by the preceding createFileUpload call.","Inspect `mutationResult.errors` whenever the id field is undefined.","Retry finalization with backoff — it can be transiently unavailable after a large upload."],"tags":["metadata-mutation","call-recorder","file-upload","graphql"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}