You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

更新vercel.json配置后应用出现404错误,求排查方案

404错误排查思路(Vercel函数配置更新后)

问题背景

按照Vercel技术支持建议更新vercel.json以设置函数maxDuration参数后,应用出现404错误,切换到FastAPI也存在同样问题。

配置文件对比

更新前的vercel.json

{"version": 2,"builds": [{ "src": "chat.py", "use": "@vercel/python" }],"routes": [{ "src": "/(.*)", "dest": "chat.py" }],"functions": {"chat.py": {"maxDuration": 200}}}

更新后的vercel.json

{"routes": [{"src": "/api/message","dest": "/api/chat.py"}],"functions": {"api/chat.py": {"maxDuration": 200}}}

相关代码

Flask后端代码

from flask import Flask, request, jsonify
import os
import time
from openai import OpenAI

# Load environment variables
from dotenv import load_dotenv
load_dotenv()

app = Flask(__name__)

# Assistant ID and Configuration
ASSISTANT_ID = "Must have TSSC"
RUN_STATUS_CHECK_INTERVAL = 0.5  # seconds
RUN_STATUS_MAX_ATTEMPTS = 600

# Initialize OpenAI client
client = OpenAI()

def handle_run_status(run, thread_id):
    for _ in range(RUN_STATUS_MAX_ATTEMPTS):
        run = client.beta.threads.runs.retrieve(
            thread_id=thread_id,
            run_id=run.id,
        )

        # Check the status of the run and return early if completed or failed
        if run.status == "completed":
            return run
        elif run.status == "failed":
            print(f"Run failed: {run.last_error}")
            return run
        elif run.status not in ["queued", "in_progress", "requires_action"]:
            print(f"Run ended with status: {run.status}")
            return run

        # Wait for a short interval before checking again
        time.sleep(RUN_STATUS_CHECK_INTERVAL)

    # If the run did not complete in the expected time, log the event and return the run object
    print("Run did not complete in expected time.")
    return run

def submit_message(assistant_id, thread_id, user_message):
    try:
        print(f"Submitting message to thread: {user_message}")
        client.beta.threads.messages.create(
            thread_id=thread_id, 
            role="user", 
            content=user_message
        )
        print("Creating run...")
        run = client.beta.threads.runs.create(
            thread_id=thread_id,
            assistant_id=assistant_id,
        )
        return run
    except Exception as e:
        print(f"Error in submit_message: {e}")
        raise

def get_response(thread_id):
    try:
        # Retrieve messages, ensuring they are in ascending order
        response = client.beta.threads.messages.list(thread_id=thread_id, order="asc")
        print(f"Retrieved messages: {response}")  # Log the retrieved messages

        # Check if response is empty or not as expected
        if not response.data:
            raise ValueError("No data in response")

        # Retrieve the latest assistant response
        # Iterate in reverse to find the latest assistant message
        for msg in reversed(response.data):
            if msg.role == "assistant":
                for content in msg.content:
                    if content.type == 'text':
                        assistant_response = content.text.value
                        print('\nAssistant output: ' + str(assistant_response) + '\n')
                        return assistant_response

        # Raise an error if no assistant response is found
        raise ValueError("No assistant responses found")

    except Exception as e:
        print(f"Error in get_response: {e}")
        raise

@app.route('/message', methods=['POST'])
def chat():
    user_message = request.json.get('message')
    thread_id = request.json.get('thread_id', None)

    try:
        if not thread_id:
            print("Creating new thread...")
            thread = client.beta.threads.create()
            thread_id = thread.id
        else:
            print(f"Retrieving thread: {thread_id}")

        run = submit_message(ASSISTANT_ID, thread_id, user_message)
        run = handle_run_status(run, thread_id)

        if run.status == 'completed':
            assistant_response = get_response(thread_id)
        else:
            assistant_response = "Assistant did not respond in time or run failed."

        print("Sending response:", {'response': assistant_response, 'thread_id': thread_id})
        return jsonify({'response': assistant_response, 'thread_id': thread_id})

    except Exception as e:
        print(f"Error in chat: {e}")
        return jsonify({'error': str(e)})

if __name__ == '__main__':
    app.run(debug=True)

Next.js路由转发代码

import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  try {
    console.log("Received request:", request); // Log incoming request

    // Forward the request to the Python backend
    console.log("Forwarding request to backend");
    const backendRes = await fetch(
      "https://rooted-gpt-chat-script.vercel.app/api/message",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(await request.json()),
      }
    );

    console.log("Received response from backend"); // Log backend response

    if (!backendRes.ok) {
      console.error("Error from backend:", backendRes.status); // Log backend error
      throw new Error(`Error from backend: ${backendRes.status}`);
    }

    // Get the response from the backend
    const data = await backendRes.json();
    console.log("Backend data:", data); // Log the data from backend

    // Send the response back to the frontend
    return NextResponse.json(data);
  } catch (error) {
    console.error("Request failed:", error); // Log error
    return new NextResponse(
      JSON.stringify({ message: "Internal Server Error" }),
      { status: 500 }
    );
  }
}

export const runtime = "edge";

排查思路

  • 文件路径匹配检查
    更新后的配置将函数路径改为api/chat.py,需确认项目中是否存在api目录且chat.py确实位于该目录下。Vercel函数路由依赖文件实际位置,路径不匹配会直接返回404。若原chat.py在根目录,需移动到api目录,或修改vercel.json中的dest路径为根目录的chat.py。

  • 路由规则与接口路径对齐
    Flask后端的接口路径是/message,但Vercel配置中将路由指向/api/message并映射到/api/chat.py,此时后端实际暴露的接口应为/api/message,而非根路径的/message。需确认Next.js转发的地址是否正确,同时检查Flask代码是否需要调整路由前缀为/api/message。可直接在Postman中访问https://<your-vercel-domain>/api/message,手动发送POST请求测试是否能触发函数。

  • Build配置缺失问题
    更新后的vercel.json移除了builds字段,Vercel可能无法正确识别Python函数的构建规则。需重新添加builds配置:

    "builds": [{ "src": "api/chat.py", "use": "@vercel/python" }]
    

    没有builds规则,Vercel可能不会将chat.py部署为Serverless函数,导致访问时返回404。

  • Vercel部署日志排查
    登录Vercel控制台,查看最新部署的日志,检查是否有构建错误或函数部署失败的提示。例如文件找不到、依赖安装失败等问题,都会导致函数无法正常部署进而返回404。

  • 函数运行时与路径验证
    确认Vercel上的函数是否成功部署:进入项目的Functions页面,查看api/chat.py是否存在且状态正常。检查函数的触发路径是否与配置的/api/message一致,Vercel有时会自动调整路由规则,需以控制台显示的实际触发路径为准。

  • Next.js转发地址的环境变量处理
    当前Next.js代码中硬编码了后端地址,部署到Vercel后,可使用环境变量(如NEXT_PUBLIC_BACKEND_URL)替代硬编码地址,避免因域名变化导致的访问错误。同时确认该地址在Vercel环境中是否可访问(Edge Runtime可能有网络访问限制)。


内容的提问来源于stack exchange,提问作者mickmedical

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 05:24:55