twentyhq/twenty · error · Error
createFileUpload mutation did not return an upload target
Error message
createFileUpload mutation did not return an upload target
What it means
Before uploading media bytes, the flow calls the metadata `createFileUpload` mutation to obtain an upload target (`fileId`, `uploadUrl`, `contentType`). If the mutation result's `createFileUpload` field is `undefined`, the server did not return an upload target, so there is nowhere to PUT the bytes and the flow throws. The genql client returns `undefined` for a field the server omitted or for a union/error shape it did not select.
Source
Thrown at packages/twenty-apps/public/call-recorder/src/logic-functions/flows/import-call-recording-media.util.ts:332
fieldMetadataUniversalIdentifier: string;
}): Promise<MediaUploadTarget> => {
const mutationResult = await metadataClient.mutation({
createFileUpload: {
__args: {
filename: fileName,
size: sizeBytes,
fileFolder: MEDIA_FILE_FOLDER,
fieldMetadataUniversalIdentifier,
},
fileId: true,
uploadUrl: true,
contentType: true,
},
});
const uploadTarget = mutationResult.createFileUpload;
if (isUndefined(uploadTarget)) {
throw new Error(
'createFileUpload mutation did not return an upload target',
);
}
return uploadTarget;
};
const completeFileUpload = async ({
metadataClient,
fileId,
}: {
metadataClient: InstanceType<typeof MetadataApiClient>;
fileId: string;
}): Promise<string> => {
const mutationResult = await metadataClient.mutation({
completeFileUpload: {
__args: { fileId },
id: true,View on GitHub (pinned to 1f5dd2bbd2)
Solutions
- Verify `CALL_RECORDING_AUDIO_FIELD_UNIVERSAL_IDENTIFIER` / `CALL_RECORDING_VIDEO_FIELD_UNIVERSAL_IDENTIFIER` resolve to existing file-type fields on the call-recording object in this workspace.
- Check the metadata client is authenticated with a role that can create file uploads on the call-recording object.
- Log the full `mutationResult` (including any `errors` array from the genql response) to see the server-side reason the field was omitted.
- Confirm `sizeBytes` is within the server's per-upload limit and that `fileFolder: 'FilesField'` is valid for this object.
Example fix
// before
const uploadTarget = mutationResult.createFileUpload;
if (isUndefined(uploadTarget)) {
throw new Error('createFileUpload mutation did not return an upload target');
}
// after — surface server errors and the requested identifier
const uploadTarget = mutationResult.createFileUpload;
if (isUndefined(uploadTarget)) {
throw new Error(
`createFileUpload mutation did not return an upload target for ${fileName} (field=${fieldMetadataUniversalIdentifier}, size=${sizeBytes}); server errors: ${JSON.stringify(mutationResult.errors ?? [])}`,
);
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: confirm the file field exists before requesting an upload target.
const fieldExists = await metadataClient.query({
fields: { __args: { filter: { universalIdentifier: { eq: fieldMetadataUniversalIdentifier } } }, edges: { node: { id: true } } },
});
if (!fieldExists.fields?.edges?.length) {
throw new Error(`File field ${fieldMetadataUniversalIdentifier} not found; cannot create upload target`);
} Type guard
const isUploadTarget = (v: unknown): v is { fileId: string; uploadUrl: string; contentType: string } =>
typeof v === 'object' &&
v !== null &&
typeof (v as any).fileId === 'string' &&
typeof (v as any).uploadUrl === 'string' &&
typeof (v as any).contentType === 'string'; Prevention
- Verify the audio/video field universal identifiers resolve to file-type fields on the call-recording object in each workspace.
- Always inspect `mutationResult.errors` when a genql field comes back undefined.
- Confirm the acting role has create-file-upload permission on the object.
- Ensure sizeBytes is within the server per-upload limit before calling createFileUpload.
When it happens
Trigger: The `fieldMetadataUniversalIdentifier` passed in does not match a file field on the object (so the server refuses to create an upload target), the authenticated user lacks permission to upload, the file size exceeds a server limit expressed as a null response, or the server returned an error payload that genql surfaces as an unset field.
Common situations: The audio/video field universal identifiers were renamed/removed; running against a workspace where the Call Recorder object/fields were not installed; permission/role misconfiguration on the installing user; a server-side upload quota reached.
Related errors
- completeFileUpload mutation did not return a file id
- upsertRowLevelPermissionPredicates returned fewer than 2 pre
- upsertRowLevelPermissionPredicates returned no predicate for
- RECALL_WEBHOOK_SECRET server variable is not set. A server a
- Raw request body was not forwarded by the server; cannot ver
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/1c9a677c7f1e94b6.
Report an issue: GitHub.