@@ -11,6 +11,8 @@ import {
1111} from '@sim/testing/mocks/providers-conversation-history.mock'
1212import { providersUtilsMock , providersUtilsMockFns } from '@sim/testing/mocks/providers-utils.mock'
1313import { beforeEach , describe , expect , it , vi } from 'vitest'
14+ import { classifyFailure } from '@/lib/core/errors/failure-log'
15+ import { isNonRetryableExecutionError } from '@/lib/execution/non-retryable-error'
1416import { executeResponsesProviderRequest } from '@/providers/openai/core'
1517import type { ProviderRequest , ProviderResponse } from '@/providers/types'
1618
@@ -113,6 +115,41 @@ describe('OpenAI non-streaming response status handling', () => {
113115 expect ( fetchMock ) . not . toHaveBeenCalled ( )
114116 } )
115117
118+ it ( 'refuses a request with no input items before sending, as a nonretryable author failure' , async ( ) => {
119+ const fetchMock = vi . fn ( ) . mockResolvedValue (
120+ jsonResponse (
121+ {
122+ error : {
123+ message :
124+ 'One of input or previous_response_id or prompt or conversation must be provided.' ,
125+ } ,
126+ } ,
127+ 400
128+ )
129+ )
130+ const error = await run ( fetchMock , {
131+ systemPrompt : '' ,
132+ messages : [
133+ { role : 'system' , content : '' } ,
134+ { role : 'user' , content : '' } ,
135+ ] ,
136+ } ) . catch ( ( caught : unknown ) => caught )
137+
138+ expect ( error ) . toMatchObject ( { name : 'EmptyProviderInputError' } )
139+ expect ( isNonRetryableExecutionError ( error ) ) . toBe ( true )
140+ expect ( classifyFailure ( error ) ) . toBe ( 'user' )
141+ expect ( fetchMock ) . not . toHaveBeenCalled ( )
142+ } )
143+
144+ it ( 'still sends a request that carries only a system prompt' , async ( ) => {
145+ const fetchMock = vi . fn ( ) . mockResolvedValue ( jsonResponse ( COMPLETED_RESPONSE ) )
146+ await run ( fetchMock , { systemPrompt : 'You are terse.' , messages : [ ] } )
147+
148+ expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 )
149+ const body = JSON . parse ( fetchMock . mock . calls [ 0 ] [ 1 ] . body )
150+ expect ( body . input ) . toEqual ( [ { role : 'system' , content : 'You are terse.' } ] )
151+ } )
152+
116153 it ( 'lets an estimated oversized request reach the provider and propagates its actual context rejection' , async ( ) => {
117154 mockConversationContext . mockReturnValue ( { agentConversation : { } } )
118155 const fetchMock = vi . fn ( ) . mockResolvedValue ( {
0 commit comments