UT: Issues with completion callback in dev

Everything is working fine in production, but I'm unable to get the onComplete callbacks to work properly in dev. I think I'm getting close, but I got the following error in console. I reached out to support via email, but I linked this thread since its a bit easier to communicate and share code via discord opposed to email. Error: Something went wrong. Please contact UploadThing and provide the following cause: "TypeError: records[0].serverData is undefined" Client side:
const { startUpload, isUploading } = useUploadThing('inputSource', {
onClientUploadComplete: (records) => {
const { url, type, uid } = records[0].serverData as {
url: string;
type: 'IMAGE' | 'VIDEO';
uid: number;
};
addUploadedInputSource(url, type, uid);
},
onUploadError: (error: Error) => {
console.error('error uploading input source', error);
toast.error('Error uploading file');
},
});
const { startUpload, isUploading } = useUploadThing('inputSource', {
onClientUploadComplete: (records) => {
const { url, type, uid } = records[0].serverData as {
url: string;
type: 'IMAGE' | 'VIDEO';
uid: number;
};
addUploadedInputSource(url, type, uid);
},
onUploadError: (error: Error) => {
console.error('error uploading input source', error);
toast.error('Error uploading file');
},
});
core:
export const uploadRouter = {
inputSource: f(
{
'image/jpeg': {
maxFileSize: '128KB',
maxFileCount: 1,
additionalProperties: {
width: 640,
height: 480,
},
},
},
{ awaitServerData: true },
)
.middleware(async () => {
const session = await auth();
console.log('session', session);
const uid = (
session.sessionClaims?.publicMetadata as { uid: number | undefined }
)?.uid;
if (!session.userId || !uid) throw new UploadThingError('UNAUTHORIZED');
return { uid };
})
.onUploadComplete(async ({ metadata, file }) => {
const { error: inputSourceError } = await tryCatch(
prisma.inputSource.create({
data: {
url: file.ufsUrl,
type: 'IMAGE',
uid: metadata.uid,
key: file.key,
},
}),
);
if (inputSourceError) {
throw new UploadThingError(
'failed to create input source: ' + inputSourceError.message,
);
}
revalidateTag(`input-sources-${metadata.uid}`);
return {
url: file.ufsUrl,
type: 'IMAGE',
uid: metadata.uid,
};
}),
} satisfies FileRouter;
export const uploadRouter = {
inputSource: f(
{
'image/jpeg': {
maxFileSize: '128KB',
maxFileCount: 1,
additionalProperties: {
width: 640,
height: 480,
},
},
},
{ awaitServerData: true },
)
.middleware(async () => {
const session = await auth();
console.log('session', session);
const uid = (
session.sessionClaims?.publicMetadata as { uid: number | undefined }
)?.uid;
if (!session.userId || !uid) throw new UploadThingError('UNAUTHORIZED');
return { uid };
})
.onUploadComplete(async ({ metadata, file }) => {
const { error: inputSourceError } = await tryCatch(
prisma.inputSource.create({
data: {
url: file.ufsUrl,
type: 'IMAGE',
uid: metadata.uid,
key: file.key,
},
}),
);
if (inputSourceError) {
throw new UploadThingError(
'failed to create input source: ' + inputSourceError.message,
);
}
revalidateTag(`input-sources-${metadata.uid}`);
return {
url: file.ufsUrl,
type: 'IMAGE',
uid: metadata.uid,
};
}),
} satisfies FileRouter;
1 Reply
shawn
shawnOP2d ago
Initially the callback in dev wasn't happening at all. I got it to happen at all by making the following changes to route:
import { createRouteHandler } from 'uploadthing/next';
import { uploadRouter } from './core';

export const runtime = 'nodejs';

export const { GET, POST } = createRouteHandler({
router: uploadRouter,
config: {
isDev: false,
},
});
import { createRouteHandler } from 'uploadthing/next';
import { uploadRouter } from './core';

export const runtime = 'nodejs';

export const { GET, POST } = createRouteHandler({
router: uploadRouter,
config: {
isDev: false,
},
});
Was able to get it to run using the following. Not ideal and I'm sure there is a better solution:
onClientUploadComplete: (records) => {
const parsedRecords = records as unknown as Array<{
url: string;
type: 'IMAGE' | 'VIDEO';
uid: number;
}>;
const { url, type, uid } = parsedRecords[0];
addUploadedInputSource(url, type, uid);
},
onClientUploadComplete: (records) => {
const parsedRecords = records as unknown as Array<{
url: string;
type: 'IMAGE' | 'VIDEO';
uid: number;
}>;
const { url, type, uid } = parsedRecords[0];
addUploadedInputSource(url, type, uid);
},

Did you find this page helpful?