UTAPI uploadFiles failing. Invalid URL

I'm building a mobile app using Expo + React Native. I'm working with server actions so that I can easily use prisma etc. The plan is to have a server action that lets me upload a file to Uploadthing. I can't use the provided expo package since all it exposes is a hook to open a camera or a file picker. In my use case this does not help me. I need to manually upload the file. That's why I landed on using the UTAPI. But I keep getting this error:
ERROR (#37) uploadFile=7ms uploadFiles=7ms utapi.#executeAsync=7ms: Failed to upload file
error: RequestError: InvalidUrl error (PUT )
...stack trace...
(/Users/.../app/node_modules/effect/dist/esm/internal/fiberRuntime.js:507:3) {
request: {
_id: '@effect/platform/HttpClientRequest',
method: 'PUT',
url: '',
urlParams: [],
hash: { _id: 'Option', _tag: 'None' },
headers: { range: 'bytes=0-', 'x-uploadthing-version': '7.7.3' },
body: {
_id: '@effect/platform/HttpBody',
_tag: 'FormData',
formData: FormData {
file: File {
size: 4539139,
type: 'image/jpeg',
name: 'blob',
lastModified: 1752267520776
}
}
}
},
reason: 'InvalidUrl',
_tag: 'RequestError',
ERROR (#37) uploadFile=7ms uploadFiles=7ms utapi.#executeAsync=7ms: Failed to upload file
error: RequestError: InvalidUrl error (PUT )
...stack trace...
(/Users/.../app/node_modules/effect/dist/esm/internal/fiberRuntime.js:507:3) {
request: {
_id: '@effect/platform/HttpClientRequest',
method: 'PUT',
url: '',
urlParams: [],
hash: { _id: 'Option', _tag: 'None' },
headers: { range: 'bytes=0-', 'x-uploadthing-version': '7.7.3' },
body: {
_id: '@effect/platform/HttpBody',
_tag: 'FormData',
formData: FormData {
file: File {
size: 4539139,
type: 'image/jpeg',
name: 'blob',
lastModified: 1752267520776
}
}
}
},
reason: 'InvalidUrl',
_tag: 'RequestError',
This is my setup (the api foler is the server actions folder):
// src/api/lib/uploadthing.ts
export const utapi = new UTApi({
token,
});
// src/api/lib/uploadthing.ts
export const utapi = new UTApi({
token,
});
// src/api/files.ts
"use server";

import { UTFile } from "uploadthing/server";
import { utapi } from "./lib/uploadthing";

export async function uploadFiles(files: { blob: Blob; name: string; type: string }[]) {
const utFiles = files.map(
(file) =>
new UTFile([file.blob], file.name, {
type: file.type,
customId: `${Date.now()}-${Math.random().toString(36).substring(7)}`,
})
);

const response = await utapi.uploadFiles(utFiles);
return response;
}
// src/api/files.ts
"use server";

import { UTFile } from "uploadthing/server";
import { utapi } from "./lib/uploadthing";

export async function uploadFiles(files: { blob: Blob; name: string; type: string }[]) {
const utFiles = files.map(
(file) =>
new UTFile([file.blob], file.name, {
type: file.type,
customId: `${Date.now()}-${Math.random().toString(36).substring(7)}`,
})
);

const response = await utapi.uploadFiles(utFiles);
return response;
}
Solution:
Fixed: Turns out this is happening because of two reasons: 1. Uploadthing uses and relies on the UPLOADTHING_TOKEN env variable even when you hardcode the token (I didn't know that)...
Jump to solution
1 Reply
Solution
Noah
Noah2mo ago
Fixed: Turns out this is happening because of two reasons: 1. Uploadthing uses and relies on the UPLOADTHING_TOKEN env variable even when you hardcode the token (I didn't know that) 2. If your env variable is not prefixed with EXPO_PUBLIC you CAN'T use it in a server action. It will always be undefined. You can only use them in Expo API Routes (I thought since server actions run on the server, you can use them) My solution was just moving the uploading from a server action to an api route. So the main gotcha for me was reason number 1. I already knew the env variable was undefined, which is why I hardocded it. But didn't realize Uploadthing still relied on it under the hood. So from my understanding the error message should have been:
env variable UPLOADTHING_TOKEN is undefined. Please define the variable for UTAPI to work

Did you find this page helpful?