fix(cli): preserve runtime recovery steps (#24800)

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
This commit is contained in:
Wooseong Kim
2026-10-23 19:20:30 -07:00
committed by GitHub
co-authored by Neil
parent 339397829e
commit fea8bb7f5b
2 changed files with 93 additions and 5 deletions
+10 -2
View File
@@ -76,7 +76,7 @@ export function formatCliError(error: unknown, context: CliErrorContext = {}): s
if (hasOrchestrationRequestId(error.data)) {
return message
}
return `${message}\nOrca is not running. Run 'orca open' first.`
return formatRuntimeUnavailableError(message, error.data)
}
// Match before generic recovery; flattened errors may retain only the conflict token.
const conflict = automationOwnerConflictRecovery(matchAutomationOwnerConflict(error))
@@ -103,7 +103,7 @@ export function formatCliError(error: unknown, context: CliErrorContext = {}): s
error instanceof RuntimeRpcFailureError &&
error.response.error.code === 'runtime_unavailable'
) {
return `${message}\nOrca is not running. Run 'orca open' first.`
return formatRuntimeUnavailableError(message, error.response.error.data)
}
if (error instanceof RuntimeRpcFailureError) {
return formatMessageWithNextSteps(message, nextStepsFromData(error.response.error.data))
@@ -119,6 +119,14 @@ function hasOrchestrationRequestId(data: unknown): boolean {
)
}
function formatRuntimeUnavailableError(message: string, data: unknown): string {
const nextSteps = nextStepsFromData(data)
if (nextSteps.length > 0) {
return formatMessageWithNextSteps(message, nextSteps)
}
return `${message}\nOrca is not running. Run 'orca open' first.`
}
export function reportCliError(error: unknown, json: boolean, context: CliErrorContext = {}): void {
const selector = contextRecovery(errorCode(error), context)
if (json) {
+83 -3
View File
@@ -252,17 +252,97 @@ describe('CLI error recovery', () => {
log.mockRestore()
})
it('preserves structured recovery for a local runtime-unavailable error', () => {
const error = new RuntimeClientError(
'runtime_unavailable',
'The local runtime has a recovery step.',
{ nextSteps: ['Run `orca runtime status --json` and retry.'] }
)
expect(formatCliError(error)).toBe(
[
'The local runtime has a recovery step.',
'Next step: Run `orca runtime status --json` and retry.'
].join('\n')
)
})
it('preserves structured recovery for an RPC runtime-unavailable error', () => {
const error = new RuntimeRpcFailureError({
id: 'req_runtime_recovery',
ok: false,
error: {
code: 'runtime_unavailable',
message: 'The runtime returned a recovery step.',
data: { nextSteps: ['Reconnect the runtime, then retry.'] }
},
_meta: { runtimeId: 'runtime_local' }
})
expect(formatCliError(error)).toBe(
[
'The runtime returned a recovery step.',
'Next step: Reconnect the runtime, then retry.'
].join('\n')
)
})
it('keeps startup guidance when a local runtime-unavailable error has no next steps', () => {
const message = 'Could not connect to the running Orca app.'
expect(formatCliError(new RuntimeClientError('runtime_unavailable', message))).toBe(
`${message}\nOrca is not running. Run 'orca open' first.`
)
})
it('keeps startup guidance when an RPC runtime-unavailable error has no next steps', () => {
const message = 'The RPC runtime is unavailable.'
const error = new RuntimeRpcFailureError({
id: 'req_rpc_startup_fallback',
ok: false,
error: {
code: 'runtime_unavailable',
message
},
_meta: { runtimeId: 'runtime_local' }
})
expect(formatCliError(error)).toBe(`${message}\nOrca is not running. Run 'orca open' first.`)
})
it('preserves the local request-id protection for an RPC runtime-unavailable error', () => {
const error = new RuntimeRpcFailureError({
id: 'req_rpc_runtime_mutation',
ok: false,
error: {
code: 'runtime_unavailable',
message: 'Re-issue the same RPC command with --retry-request mutation_2.',
data: {
orchestrationRequestId: 'mutation_2',
nextSteps: ['Read the RPC mutation status before retrying.']
}
},
_meta: { runtimeId: 'runtime_local' }
})
expect(formatCliError(error)).toBe(
'Re-issue the same RPC command with --retry-request mutation_2.'
)
})
it('does not replace mutation recovery with generic runtime startup advice', () => {
const error = new RuntimeClientError(
'runtime_unavailable',
'Re-issue the same command with --retry-request mutation_1.',
{ orchestrationRequestId: 'mutation_1' }
{
orchestrationRequestId: 'mutation_1',
nextSteps: ['Read the mutation request status before retrying.']
}
)
const output = formatCliError(error)
expect(output).toContain('--retry-request mutation_1')
expect(output).not.toContain('orca open')
expect(output).toBe('Re-issue the same command with --retry-request mutation_1.')
})
})