Skip to content

Conversation

@Jhonattan2121
Copy link
Collaborator

@Jhonattan2121 Jhonattan2121 commented Dec 5, 2025

Summary

  • Added schema-driven validation for the Venice background route to reject empty or oversized payloads with precise 400 errors instead of letting them hit the LLM.
  • Centralized JSON parsing and error handling in parseUserInput, ensuring we never log or forward malformed content.
  • Enforced the 2 000-character ceiling end-to-end by slicing the validated text before any logging or API calls, preventing runaway token costs and server timeouts.

Detail

  1. Strict request contract
    Introduced zod in src/app/api/venice/background/route.ts and defined requestSchema that trims, validates presence, and caps text at 2 000 chars. Custom BadRequestError ensures consistent HTTP 400 responses.

  2. Hardened parsing path
    parseUserInput now owns JSON decoding, schema validation, and error translation. All parsing failures (invalid JSON, missing text, size overrun) are surfaced early without touching Venice.

  3. Bounded context before execution
    Even valid inputs are sliced to MAX_INPUT_CHARS before being interpolated into PROMPT, guaranteeing logs and Venice calls never exceed the agreed ceiling.

Summary by CodeRabbit

  • Bug Fixes

    • Stronger input validation and clearer errors for invalid or malformed requests.
    • Enforced input size limits to prevent oversized submissions and ensure stable processing.
  • Improvements

    • More descriptive, user-facing error messages (toasts) when background generation fails.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 5, 2025 17:29
@vercel
Copy link

vercel bot commented Dec 5, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
nounspace-ts Ready Ready Preview, Comment Dec 18, 2025 0:58am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 5, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Added JSON input validation, error handling, and truncation to the Venice background API route; adjusted BackgroundGenerator fetch error handling to extract and surface server error messages to the user.

Changes

Cohort / File(s) Summary
API: Input validation & truncation
src/app/api/venice/background/route.ts
Added MAX_INPUT_CHARS, requestSchema, and BadRequestError; implemented parseUserInput() to parse & validate JSON and centralize errors; enforce input truncation (truncatedInput) when building system prompt; standardized error responses and logging for parsing/validation failures.
Client: Fetch error extraction & user toast
src/common/lib/theme/BackgroundGenerator.tsx
On non-OK fetch responses, read JSON or text to extract an error/message field (or fallback) and throw a descriptive Error; catch block now shows a user-facing toast with the error message or a generic message.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review parseUserInput() for JSON error handling and schema correctness.
  • Verify truncation (MAX_INPUT_CHARS) is applied where intended and doesn't break prompt semantics.
  • Confirm error logging and HTTP status handling in the route for different failure modes.
  • Inspect BackgroundGenerator fetch error parsing to ensure no edge cases (non-JSON bodies) are missed.

Poem

🐰 I nibble bytes and watch them trim,

I tidy inputs on a whim.
Shorter lines hop through the gate,
Clean and safe — that's simply great.
A tiny rabbit guards the gate, input neat and up-to-date.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing input validation and guards for Venice background processing to reject invalid or oversized payloads.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/venice-input-limits

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds input validation and sanitization to the Venice background API route to prevent malformed requests and control API costs. The implementation uses zod schemas to validate incoming requests, reject invalid payloads early with clear 400 errors, and enforce a 2,000-character limit on user input before it reaches the LLM.

  • Introduced schema-based validation with zod to enforce input constraints (presence, size limits)
  • Centralized JSON parsing and error handling in a dedicated parseUserInput function
  • Added custom BadRequestError class for consistent HTTP 400 responses

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

console.error("Unexpected error parsing request body:", error);
return new Response("Invalid request body", { status: 400 });
}
const truncatedInput = userInput.slice(0, MAX_INPUT_CHARS); // never log or send more than the limit
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The truncation on line 36 is redundant. The requestSchema validation on line 10 already enforces a maximum length of 2000 characters with .max(MAX_INPUT_CHARS, ...), which means any input exceeding this limit will be rejected before reaching this point. The parseUserInput function will throw a BadRequestError if the input is too long, so userInput is guaranteed to be within the limit. This line can be removed, and truncatedInput can be replaced with userInput throughout.

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +105
async function parseUserInput(request: Request) {
let payload: unknown;
try {
payload = await request.json();
} catch {
throw new BadRequestError("Invalid JSON payload");
}
const result = requestSchema.safeParse(payload);
if (!result.success) {
const issue = result.error.issues.at(0);
throw new BadRequestError(issue?.message ?? "Invalid request body");
}
return result.data.text;
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new parseUserInput function and validation logic should have test coverage. Consider adding tests to verify:

  • Invalid JSON payloads are rejected with "Invalid JSON payload"
  • Missing text field is rejected with "User input is missing"
  • Empty or whitespace-only text is rejected (after trim)
  • Input exceeding 2000 characters is rejected with the appropriate error message
  • Valid input is correctly parsed and returned

The project uses vitest (see package.json), and similar validation patterns in other API routes (e.g., src/pages/api/notifications/index.ts) could serve as examples for structuring these tests.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/app/api/venice/background/route.ts (2)

26-36: Centralized parsing + truncation behavior look correct (note minor redundancy)

The parseUserInput try/catch and BadRequestError handling give you a clean separation between user mistakes and unexpected failures, and returning a 400 with a specific message matches the PR goals.

truncatedInput = userInput.slice(0, MAX_INPUT_CHARS) is technically redundant because requestSchema.max(MAX_INPUT_CHARS) already guarantees userInput.length <= MAX_INPUT_CHARS, but it’s harmless and slightly future‑proof if the schema changes later. No changes strictly required here.


1-17: Validation + error handling are solid; optional refinements suggested

The overall flow—schema-based validation, central parseUserInput, and BadRequestError for 400s—is clean and will reliably reject empty/oversized/malformed payloads before hitting Venice.

zod (v3.22.4) is already in your dependencies, so no concerns there.

One optional polish suggestion:

Optional: Normalize validation error surface
You're currently surfacing the first zod issue's raw .message. That's fine, but if you ever want a more stable API surface, you might instead normalize all validation errors to a generic "Invalid request body" and log result.error server-side for debugging. parseUserInput already infers Promise<string>, but you could make this explicit for stronger type contracts if preferred.

This is purely optional; the current implementation is correct and readable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2c8130 and 31b5e67.

📒 Files selected for processing (1)
  • src/app/api/venice/background/route.ts (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/api/venice/background/route.ts (1)
src/app/api/venice/config.ts (1)
  • VENICE_API_KEY (7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: CodeQL analysis (javascript-typescript)
  • GitHub Check: Agent
🔇 Additional comments (1)
src/app/api/venice/background/route.ts (1)

58-58: Using truncatedInput in the prompt achieves the intended ceiling

Swapping in truncatedInput for the placeholder keeps the user context within the configured character limit before it ever reaches Venice, aligning with the “end‑to‑end 2,000‑character ceiling” requirement without altering the rest of the prompt logic.

@willyogo
Copy link
Member

good thinking to guard the inputs here and show useful error messages to the user, but after quick testing it's clear that this is not ready for prod. i wasn't able to notice any improvements from the tests i ran, but I did notice things that work currently in prod but do not work in this branch, plus fail silently with no error message in the UI (example below). the error from the logs is that the input exceeds 2,000 characters, but using the same input works in prod.

where did the character limit come from? why is there no error message displayed in the UI?

please figure out a reasonable character limit (2k is way too low) and ensure that an error message is displayed to the user if they click 'generate' and have exceeded it.

2025-12-16 16 02 39

ie. generating with the following context works in prod but not this branch:

please remove the moving dots, and bring the background to life

<style> body, html { margin: 0; padding: 0; height: 100%; overflow: hidden; }
    .background {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: linear-gradient(135deg, #6a0dad, #9400d3);
        animation: gradientAnimation 10s ease infinite;
    }

    .sparkles {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        overflow: hidden;
    }

    .sparkle {
        position: absolute;
        background: radial-gradient(circle, #ffffff, transparent);
        opacity: 0.8;
        pointer-events: none;
        animation: sparkleAnimation 2s ease-in-out infinite;
    }

    .sparkle:nth-child(1) {
        width: 2px;
        height: 2px;
        left: 20%;
        top: 20%;
        animation-duration: 1s;
        animation-delay: 0.1s;
    }

    .sparkle:nth-child(2) {
        width: 3px;
        height: 3px;
        left: 30%;
        top: 50%;
        animation-duration: 1.5s;
        animation-delay: 0.2s;
    }

    .sparkle:nth-child(3) {
        width: 1.5px;
        height: 1.5px;
        left: 50%;
        top: 30%;
        animation-duration: 2s;
        animation-delay: 0.3s;
    }

    .sparkle:nth-child(4) {
        width: 2.5px;
        height: 2.5px;
        left: 70%;
        top: 40%;
        animation-duration: 2.5s;
        animation-delay: 0.4s;
    }

    .sparkle:nth-child(5) {
        width: 2px;
        height: 2px;
        left: 80%;
        top: 70%;
        animation-duration: 1.8s;
        animation-delay: 0.5s;
    }

    @keyframes gradientAnimation {
        0% {
            background-position: 0% 50%;
        }
        50% {
            background-position: 100% 50%;
        }
        100% {
            background-position: 0% 50%;
        }
    }

    @keyframes sparkleAnimation {
        0%, 100% {
            transform: scale(1) translateY(0);
            opacity: 0.8;
        }
        50% {
            transform: scale(1.5) translateY(-20px);
            opacity: 0.6;
        }
    }
</style>

@willyogo willyogo marked this pull request as draft December 16, 2025 22:23
…messages

- Increase character limit from 2000 to 10000 to support complex HTML/CSS inputs
- Add proper error handling in frontend to display error messages via toast
- Update API to return JSON error responses for better error handling
- Fix silent failures - users now see clear error messages when validation fails
- Upgrade Next.js from 15.2.2 to 15.5.9
- Fixes critical security vulnerability CVE-2025-66478
- Prevents remote code execution in React Server Components
@Jhonattan2121
Copy link
Collaborator Author

@willyogo

@Jhonattan2121 Jhonattan2121 marked this pull request as ready for review December 18, 2025 20:15
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/app/api/venice/background/route.ts (1)

42-42: Redundant truncation after schema validation.

The requestSchema already enforces max(MAX_INPUT_CHARS), so any input reaching this point is guaranteed to be within the limit. The .slice() is defensive but unnecessary given the current flow.

If you want to keep it as a defense-in-depth measure (guarding against future refactors that might bypass validation), that's acceptable. Otherwise, you can simplify by using userInput directly.

🧹 Nitpick comments (3)
src/app/api/venice/background/route.ts (3)

12-17: Consider setting the name property for better error identification.

Custom error classes should set this.name to improve stack traces and error identification in logs.

🔎 Suggested improvement
 class BadRequestError extends Error {
   status = 400;
   constructor(message: string) {
     super(message);
+    this.name = "BadRequestError";
   }
 }

22-24: Consider returning 500 for missing API key.

A missing VENICE_API_KEY is a server configuration error, not a client error. Status 500 (Internal Server Error) or 503 (Service Unavailable) would be more semantically accurate than 400.

🔎 Suggested change
   if (!VENICE_API_KEY) {
-    return new Response("API key is missing", { status: 400 });
+    console.error("VENICE_API_KEY environment variable is not configured");
+    return new Response("Service temporarily unavailable", { status: 503 });
   }

72-75: Consider adding a timeout to the Venice API fetch.

The external fetch has no explicit timeout. While maxDuration = 300 limits the overall request, a hung connection to Venice could consume the entire budget on the first model, preventing fallback attempts.

🔎 Add AbortController timeout
+    const controller = new AbortController();
+    const timeoutId = setTimeout(() => controller.abort(), 60000); // 60s per model
     const fetchResponse = await fetch(
       "https://api.venice.ai/api/v1/chat/completions",
-      options,
+      { ...options, signal: controller.signal },
     );
+    clearTimeout(timeoutId);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31b5e67 and 3debe01.

📒 Files selected for processing (2)
  • src/app/api/venice/background/route.ts (4 hunks)
  • src/common/lib/theme/BackgroundGenerator.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/api/venice/background/route.ts (1)
src/app/api/venice/config.ts (1)
  • VENICE_API_KEY (7-7)
🔇 Additional comments (5)
src/common/lib/theme/BackgroundGenerator.tsx (2)

100-116: LGTM! Robust error extraction with appropriate fallbacks.

The error handling correctly prioritizes JSON extraction, falls back to plain text, and uses statusText as a last resort. This addresses the PR reviewer's concern about missing UI feedback when requests fail.


127-136: Good addition of user-facing error feedback.

The toast now displays the extracted error message, providing clear feedback to users when background generation fails. This directly addresses the reviewer's request for visible UI errors.

One minor note: the 8-second duration is reasonable, but consider whether this is consistent with other error toasts in the application.

src/app/api/venice/background/route.ts (3)

1-11: Good schema-driven validation setup.

The zod schema correctly trims input, enforces presence (min 1), and caps at the limit. The increased 10,000 character limit addresses the reviewer's concern about complex HTML/CSS inputs being rejected.


26-41: Solid error handling with structured JSON responses.

The try-catch correctly distinguishes BadRequestError from unexpected errors, returning structured JSON that the frontend can parse and display. This aligns well with the BackgroundGenerator.tsx changes that extract error messages from JSON responses.


98-111: Clean input parsing with proper validation.

The parseUserInput function correctly:

  • Catches JSON parsing errors
  • Uses safeParse to avoid exceptions from zod
  • Extracts the first issue message for user-friendly errors

@willyogo willyogo added the LGFTP Looks Good From Testing Perspective label Dec 19, 2025
@willyogo
Copy link
Member

looks great now from testing perspective, ty @Jhonattan2121

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

LGFTP Looks Good From Testing Perspective

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants