star7th/showdoc · error · Error
AI 生成失败
Error message
AI 生成失败
What it means
Generic message thrown by the AI-generation modal (handleGenerate) when the POST to {serverHost}/api/ai/create (form-urlencoded body with content + user_token) returns a non-OK status. The real status is discarded, so 'AI 生成失败' ('AI generation failed') covers everything from auth rejection to a PHP 500. Server-side, AiController::create() first calls requireLoginUser(); note that a missing admin open_api_key is NOT this error — that case returns 200 with an SSE-formatted error stream.
Source
Thrown at web_src/src/views/modals/page/AIModal/index.vue:163
// 获取用户 token
const userInfo = getUserInfoFromStorage()
if (userInfo && userInfo.user_token) {
jsonBody.user_token = userInfo.user_token
}
// 使用 fetch 实现流式响应
const url = getServerHost() + '/api/ai/create'
const response = await fetch(url, {
method: 'POST',
body: new URLSearchParams(jsonBody),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
if (!response.ok) {
throw new Error('AI 生成失败')
}
const reader = response.body?.getReader()
const decoder = new TextDecoder('utf-8')
let result = ''
if (reader) {
const readChunk = async () => {
const { value, done } = await reader.read()
if (!done) {
const dataString = decoder.decode(value)
const lines = dataString.trim().split('data: ')
for (const line of lines) {
if (line.trim() !== '') {
try {
const data = JSON.parse(line.replace('data: ', ''))View on GitHub (pinned to 6a3fa91eee)
Solutions
- Include the status in the thrown error (`throw new Error(`AI 生成失败 (HTTP ${response.status})`)`) and branch on it: 401/403 -> re-login, 404 -> check server host/route, 5xx -> check server logs.
- Verify getUserInfoFromStorage() returned a fresh user_token before fetching; if absent, force login instead of posting without credentials.
- Confirm the server actually serves /api/ai/create on the host returned by getServerHost() (curl the endpoint with the same form body).
- If 5xx, inspect the PHP server log — the same request also triggers server/app/Common/bootstrap.php, whose DB failure produces 500s for every API call.
- For streaming errors that DO arrive with status 200, parse the SSE `data:` line containing error_code/error_message and surface that text instead of this generic throw.
Example fix
// before
if (!response.ok) {
throw new Error('AI 生成失败')
}
// after
if (!response.ok) {
const detail = await response.text().catch(() => '')
throw new Error(`AI 生成失败 (HTTP ${response.status}${response.statusText ? ' ' + response.statusText : ''}) ${detail.slice(0, 120)}`.trim())
} Defensive patterns
Strategy: try-catch
Validate before calling
const userInfo = getUserInfoFromStorage()
if (!userInfo?.user_token) {
outputContent.value = ''
generating.value = false
// prompt login instead of firing a doomed request
throw new Error('请先登录后再使用 AI 生成')
} Type guard
function isAiGenerateFailure(e: unknown): e is Error {
return e instanceof Error && e.message.startsWith('AI 生成失败')
} Try / catch
try {
const response = await fetch(url, { /* ... */ })
if (!response.ok) {
if (response.status === 401 || response.status === 403) throw new Error('登录已过期,请重新登录后再试')
throw new Error(`AI 生成失败 (HTTP ${response.status})`)
}
// ...
} catch (e) {
generating.value = false
outputContent.value = e instanceof Error ? e.message : 'AI 生成失败'
} Prevention
- Never discard response.status — embed it in the error so 401 vs 500 is distinguishable in bug reports.
- Gate the generate button on a verified logged-in state (userInfo.user_token present).
- Remember config errors (missing admin AI key, project AI disabled) arrive as HTTP 200 SSE error payloads — parse data: lines for error_code/error_message rather than relying on !response.ok alone.
- In tests, mock fetch with realistic Response(url-encoded body, status) objects that include a body stream.
When it happens
Trigger: user_token missing (getUserInfoFromStorage() returned nothing) or expired -> requireLoginUser() rejects with 401/403; getServerHost() misconfigured or server rolled back -> 404; PHP fatal/bootstrap RuntimeException (DB unavailable) -> 500; reverse-proxy failure -> 502/504; logged-in user whose session was invalidated server-side but still present in localStorage.
Common situations: User pastes localStorage/cookie data from another environment so user_token does not match the server; server upgraded and the /api/ai route namespace changed; SQLite file unreadable making every request 500; admin assumes this error means the AI key is missing, but that configuration mistake actually arrives as a 200 SSE error message ('管理员没有在管理后台配置AI助手认证KEY...') rendered into the output area instead.
Related errors
AI-assisted analysis of star7th/showdoc@6a3fa91eee (2026-08-21).
Data as JSON: /api/errors/eb1fef85318f0580.
Report an issue: GitHub.