lobehub/lobehub · error · TRPCError

BAD_REQUEST

BAD_REQUEST

Error message

File size cannot be negative

What it means

Thrown by file.createFile (file.ts:238) when actualSize is negative after the metadata-fetch fallback. actualSize starts as input.size and is only overridden by the URL's Content-Length when that value is >= 1; therefore a negative actualSize means input.size was negative (the schema does not bound it below zero) OR a misconfigured server returned a negative Content-Length that the >= 1 guard would actually NOT adopt — so in practice this is a client passing a negative size. businessFileUploadCheck is called first (for telemetry/abuse signal) then the BAD_REQUEST is thrown.

Solutions

  1. On the client, compute size from the actual File/Blob (.size) and guard `if (size < 0) throw` before calling the RPC.
  2. If the size is genuinely unknown, pass 0 (the server will then prefer the URL's Content-Length when >= 1) rather than a negative sentinel.
  3. Validate the upload schema with .nonnegative() on size at the zod layer in the client.

Example fix

// before
await trpc.file.createFile.mutate({ ..., size: -1 /* unknown */ });
// after
const size = file?.size ?? 0;
if (size < 0) throw new Error('computed file size is negative');
await trpc.file.createFile.mutate({ ..., size });
Defensive patterns

Strategy: validation

Validate before calling

const size = file?.size ?? 0;
if (!Number.isFinite(size) || size < 0) {
  throw new Error('computed file size is invalid');
}
await trpc.file.createFile.mutate({ ..., size });

Type guard

function isNonNegativeSize(n: unknown): n is number {
  return typeof n === 'number' && Number.isFinite(n) && n >= 0;
}

Prevention

When it happens

Trigger: Client calling file.createFile with input.size < 0 (e.g. a signed-int overflow, a default -1 'unknown size' sentinel, or a bug computing size from a failed stat). The metadata-fetch step does not rescue it because Content-Length only overrides when >= 1.

Common situations: Client using -1 to mean 'size unknown' instead of omitting the field; integer underflow when subtracting offsets; size derived from a failed HEAD request that left a NaN/negative in a downstream calculation.

Understand the failure class

Background: BAD_REQUEST error code: request rejected as invalid (HTTP 400) - causes and fixes across libraries — this error's family across 8 libraries.

Related errors


AI-assisted analysis of lobehub/lobehub@10f24d7ade (2026-08-12). Data as JSON: /api/errors/29484729d8d26183. Report an issue: GitHub.

Appendix: source

Thrown at apps/server/src/routers/lambda/file.ts:238

      try {
        const { contentLength } = await ctx.fileService.getFileMetadata(input.url);
        if (contentLength >= 1) {
          actualSize = contentLength;
        }
      } catch {
        // If metadata fetch fails, use original size from input
      }

      if (actualSize < 0) {
        await businessFileUploadCheck({
          actualSize,
          clientIp: ctx.clientIp ?? undefined,
          inputSize: input.size,
          url: input.url,
          userId: ctx.userId,
          workspaceId: ctx.workspaceId,
        });
        throw new TRPCError({ code: 'BAD_REQUEST', message: 'File size cannot be negative' });
      }

      const { id } = await ctx.serverDB.transaction(async (trx) => {
        await businessFileUploadCheck({
          actualSize,
          clientIp: ctx.clientIp ?? undefined,
          inputSize: input.size,
          transaction: trx,
          url: input.url,
          userId: ctx.userId,
          workspaceId: ctx.workspaceId,
        });

        let shouldRefreshGlobalFile = false;
        if (isExist && existingFile.url && existingFile.url !== input.url) {
          shouldRefreshGlobalFile = !(await isStoredObjectAvailable(
            ctx.fileService,
            existingFile.url,

View on GitHub (pinned to 10f24d7ade)