mirror of
https://github.com/langgenius/dify.git
synced 2026-06-16 22:11:09 +08:00
feat(cli): render server error details and semantic code without -v
Surfaces server semantic code (server.code ?? e.code), per-field validation details, and the winning hint (server.hint ?? e.hint) in human output so a 422 user sees actionable context without needing --verbose.
This commit is contained in:
parent
f3dde631dd
commit
32809403d6
92
cli/src/errors/format.test.ts
Normal file
92
cli/src/errors/format.test.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { HttpClientError } from './base'
|
||||
import { ErrorCode } from './codes'
|
||||
import { formatErrorForCli } from './format'
|
||||
|
||||
function validationError(): HttpClientError {
|
||||
return new HttpClientError({
|
||||
code: ErrorCode.Server4xxOther,
|
||||
message: 'Request validation failed',
|
||||
httpStatus: 422,
|
||||
serverError: {
|
||||
code: 'invalid_param',
|
||||
message: 'Request validation failed',
|
||||
status: 422,
|
||||
hint: 'check the page parameter',
|
||||
details: [
|
||||
{ type: 'int_parsing', loc: ['page'], msg: 'must be >= 1' },
|
||||
{ type: 'missing', loc: ['inputs', 'query'], msg: 'field required' },
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe('formatErrorForCli — human', () => {
|
||||
it('prints server code, message, and details without verbose', () => {
|
||||
const out = formatErrorForCli(validationError(), { isErrTTY: false })
|
||||
|
||||
expect(out).toContain('invalid_param: Request validation failed')
|
||||
expect(out).toContain('- page: must be >= 1 (int_parsing)')
|
||||
expect(out).toContain('- inputs.query: field required (missing)')
|
||||
expect(out).toContain('check the page parameter')
|
||||
expect(out).not.toContain('raw_response')
|
||||
})
|
||||
|
||||
it('falls back to cli code when no server code', () => {
|
||||
const err = new HttpClientError({ code: ErrorCode.Server5xx, message: 'server error (HTTP 502)', httpStatus: 502 })
|
||||
|
||||
const out = formatErrorForCli(err, { isErrTTY: false })
|
||||
|
||||
expect(out).toContain('server_5xx: server error (HTTP 502)')
|
||||
})
|
||||
|
||||
it('server hint wins over cli hint; cli hint fills when server sent none', () => {
|
||||
// validationError has server hint "check the page parameter"; cli hint is ignored
|
||||
const withCliHint = new HttpClientError({
|
||||
code: ErrorCode.Server4xxOther,
|
||||
message: 'Request validation failed',
|
||||
httpStatus: 422,
|
||||
hint: 'cli fallback hint',
|
||||
serverError: {
|
||||
code: 'invalid_param',
|
||||
message: 'Request validation failed',
|
||||
status: 422,
|
||||
hint: 'check the page parameter',
|
||||
details: [],
|
||||
},
|
||||
})
|
||||
expect(formatErrorForCli(withCliHint, { isErrTTY: false })).toContain('check the page parameter')
|
||||
expect(formatErrorForCli(withCliHint, { isErrTTY: false })).not.toContain('cli fallback hint')
|
||||
|
||||
// no server hint → cli hint shown
|
||||
const noServerHint = new HttpClientError({
|
||||
code: ErrorCode.AuthExpired,
|
||||
message: 'session expired',
|
||||
hint: 'run difyctl auth login',
|
||||
})
|
||||
expect(formatErrorForCli(noServerHint, { isErrTTY: false })).toContain('run difyctl auth login')
|
||||
})
|
||||
|
||||
it('renders request and http_status lines', () => {
|
||||
const err = new HttpClientError({
|
||||
code: ErrorCode.Server5xx,
|
||||
message: 'upstream boom',
|
||||
httpStatus: 502,
|
||||
method: 'GET',
|
||||
url: 'https://api.dify.ai/v1/me',
|
||||
})
|
||||
const out = formatErrorForCli(err, { isErrTTY: false })
|
||||
expect(out).toContain('request: GET https://api.dify.ai/v1/me')
|
||||
expect(out).toContain('http_status: 502')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatErrorForCli — json', () => {
|
||||
it('envelope nests the whole server error', () => {
|
||||
const out = JSON.parse(formatErrorForCli(validationError(), { format: 'json' }))
|
||||
|
||||
expect(out.error.server.code).toBe('invalid_param')
|
||||
expect(out.error.server.details).toHaveLength(2)
|
||||
expect(out.error.code).toBe('server_4xx_other')
|
||||
})
|
||||
})
|
||||
@ -47,9 +47,14 @@ function renderEnvelope(env: ErrorEnvelope): string {
|
||||
function renderHuman(env: ErrorEnvelope, isErrTTY: boolean): string {
|
||||
const cs = colorScheme(colorEnabled(isErrTTY))
|
||||
const e = env.error
|
||||
const lines: string[] = [`${e.code}: ${e.message}`]
|
||||
if (e.hint !== undefined)
|
||||
lines.push(`${cs.magenta('hint:')} ${cs.cyan(e.hint)}`)
|
||||
const server = e.server
|
||||
const headerCode = server?.code ?? e.code
|
||||
const lines: string[] = [`${headerCode}: ${e.message}`]
|
||||
for (const d of server?.details ?? [])
|
||||
lines.push(` - ${(d.loc ?? []).join('.')}: ${d.msg} (${d.type})`)
|
||||
const hint = server?.hint ?? e.hint
|
||||
if (hint !== undefined && hint !== null)
|
||||
lines.push(`${cs.magenta('hint:')} ${cs.cyan(hint)}`)
|
||||
if (e.method !== undefined && e.url !== undefined)
|
||||
lines.push(`request: ${e.method} ${e.url}`)
|
||||
if (e.http_status !== undefined)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user