mirror of
https://github.com/stablyai/orca.git
synced 2026-09-27 00:02:37 +00:00
Improve PTY exhaustion recovery messages (#1931)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { addNodePtyRecoveryHint, parseNodePtyDiagnostic } from './node-pty-error-hints'
|
||||
|
||||
const PTY_ALLOCATION_HINT = [
|
||||
'Your system cannot allocate any more pty devices.',
|
||||
'',
|
||||
'Orca requires a pty device to launch a new terminal. This error is usually due to having too many terminal windows or terminal sessions open, either in Orca or another program.',
|
||||
'',
|
||||
'Free up some pty devices and try again.'
|
||||
].join('\n')
|
||||
|
||||
const TERMINAL_PROCESS_LIMIT_HINT = [
|
||||
'Your system cannot start another terminal process.',
|
||||
'',
|
||||
'This is usually due to having too many terminal sessions or other processes running.',
|
||||
'',
|
||||
'Close unused terminals or quit unused processes and try again.'
|
||||
].join('\n')
|
||||
|
||||
describe('node-pty diagnostic error hints', () => {
|
||||
it('parses the native step and errno without dropping the original message', () => {
|
||||
const message =
|
||||
@@ -16,18 +32,42 @@ describe('node-pty diagnostic error hints', () => {
|
||||
const message =
|
||||
"node-pty: open_slave failed: EMFILE (errno 24, Too many open files) - slave='/dev/ttys003'"
|
||||
|
||||
expect(addNodePtyRecoveryHint(message)).toBe(
|
||||
`Daemon hit the file-descriptor limit. Restart the daemon. ${message}`
|
||||
)
|
||||
expect(addNodePtyRecoveryHint(message)).toBe(`${PTY_ALLOCATION_HINT} ${message}`)
|
||||
})
|
||||
|
||||
it('hints when the system cannot allocate a pty master', () => {
|
||||
const message =
|
||||
'node-pty: posix_openpt failed: ENFILE (errno 23, Too many open files in system)'
|
||||
|
||||
expect(addNodePtyRecoveryHint(message)).toBe(`${PTY_ALLOCATION_HINT} ${message}`)
|
||||
})
|
||||
|
||||
it('hints local wrapped spawn errors from pty allocation failures', () => {
|
||||
const message =
|
||||
'Failed to spawn shell "/bin/zsh": node-pty: open_slave failed: EMFILE (errno 24, Too many open files) - slave=\'/dev/ttys003\' (shell: /bin/zsh, cwd: /tmp, arch: arm64, platform: darwin 25.0.0). If this persists, please file an issue.'
|
||||
|
||||
expect(addNodePtyRecoveryHint(message)).toBe(`${PTY_ALLOCATION_HINT} ${message}`)
|
||||
})
|
||||
|
||||
it('hints unstructured openpty allocation failures', () => {
|
||||
const message = 'Failed to spawn shell "/bin/bash": openpty(3) failed.'
|
||||
|
||||
expect(addNodePtyRecoveryHint(message)).toBe(`${PTY_ALLOCATION_HINT} ${message}`)
|
||||
})
|
||||
|
||||
it('hints when posix_spawn reports the per-user process limit', () => {
|
||||
const message =
|
||||
"node-pty: posix_spawn failed: EAGAIN (errno 35, Resource temporarily unavailable) - helper='/tmp/node-pty/spawn-helper'"
|
||||
|
||||
expect(addNodePtyRecoveryHint(message)).toBe(
|
||||
`Per-user process limit reached. Quit some agents and retry. ${message}`
|
||||
)
|
||||
expect(addNodePtyRecoveryHint(message)).toBe(`${TERMINAL_PROCESS_LIMIT_HINT} ${message}`)
|
||||
})
|
||||
|
||||
it('does not duplicate an existing recovery hint', () => {
|
||||
const message =
|
||||
"node-pty: open_slave failed: EMFILE (errno 24, Too many open files) - slave='/dev/ttys003'"
|
||||
const hinted = `${PTY_ALLOCATION_HINT} ${message}`
|
||||
|
||||
expect(addNodePtyRecoveryHint(hinted)).toBe(hinted)
|
||||
})
|
||||
|
||||
it('leaves unrelated and unhinted node-pty diagnostics unchanged', () => {
|
||||
|
||||
@@ -4,9 +4,45 @@ export type NodePtyDiagnostic = {
|
||||
}
|
||||
|
||||
const NODE_PTY_DIAGNOSTIC_RE = /^node-pty: ([A-Za-z0-9_]+) failed: .*?\(errno (\d+)(?:, [^)]*)?\)/
|
||||
const NODE_PTY_DIAGNOSTIC_ANYWHERE_RE =
|
||||
/node-pty: ([A-Za-z0-9_]+) failed: .*?\(errno (\d+)(?:, [^)]*)?\)/
|
||||
const GENERIC_PTY_ALLOCATION_RE = /\b(?:openpty|forkpty)\(3\) failed\b/i
|
||||
|
||||
const PTY_ALLOCATION_STEPS = new Set([
|
||||
'posix_openpt',
|
||||
'grantpt',
|
||||
'unlockpt',
|
||||
'ioctl_TIOCPTYGNAME',
|
||||
'open_slave'
|
||||
])
|
||||
|
||||
const RESOURCE_EXHAUSTION_ERRNOS = new Set([
|
||||
11, // EAGAIN on Linux
|
||||
12, // ENOMEM
|
||||
23, // ENFILE on macOS
|
||||
24, // EMFILE on macOS/Linux
|
||||
35 // EAGAIN on macOS
|
||||
])
|
||||
|
||||
const PTY_ALLOCATION_HINT = [
|
||||
'Your system cannot allocate any more pty devices.',
|
||||
'',
|
||||
'Orca requires a pty device to launch a new terminal. This error is usually due to having too many terminal windows or terminal sessions open, either in Orca or another program.',
|
||||
'',
|
||||
'Free up some pty devices and try again.'
|
||||
].join('\n')
|
||||
|
||||
const TERMINAL_PROCESS_LIMIT_HINT = [
|
||||
'Your system cannot start another terminal process.',
|
||||
'',
|
||||
'This is usually due to having too many terminal sessions or other processes running.',
|
||||
'',
|
||||
'Close unused terminals or quit unused processes and try again.'
|
||||
].join('\n')
|
||||
|
||||
export function parseNodePtyDiagnostic(message: string): NodePtyDiagnostic | null {
|
||||
const match = NODE_PTY_DIAGNOSTIC_RE.exec(message)
|
||||
const match =
|
||||
NODE_PTY_DIAGNOSTIC_RE.exec(message) ?? NODE_PTY_DIAGNOSTIC_ANYWHERE_RE.exec(message)
|
||||
if (!match) {
|
||||
return null
|
||||
}
|
||||
@@ -21,11 +57,14 @@ export function getNodePtyRecoveryHint(diagnostic: NodePtyDiagnostic): string |
|
||||
if (diagnostic.step === 'posix_spawn' && diagnostic.errno === 2) {
|
||||
return "Daemon's node-pty install is gone (worktree deleted?). Restart Orca."
|
||||
}
|
||||
if (diagnostic.step === 'open_slave' && diagnostic.errno === 24) {
|
||||
return 'Daemon hit the file-descriptor limit. Restart the daemon.'
|
||||
if (
|
||||
PTY_ALLOCATION_STEPS.has(diagnostic.step) &&
|
||||
RESOURCE_EXHAUSTION_ERRNOS.has(diagnostic.errno)
|
||||
) {
|
||||
return PTY_ALLOCATION_HINT
|
||||
}
|
||||
if (diagnostic.step === 'posix_spawn' && diagnostic.errno === 35) {
|
||||
return 'Per-user process limit reached. Quit some agents and retry.'
|
||||
if (diagnostic.step === 'posix_spawn' && RESOURCE_EXHAUSTION_ERRNOS.has(diagnostic.errno)) {
|
||||
return TERMINAL_PROCESS_LIMIT_HINT
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -33,9 +72,15 @@ export function getNodePtyRecoveryHint(diagnostic: NodePtyDiagnostic): string |
|
||||
export function addNodePtyRecoveryHint(message: string): string {
|
||||
const diagnostic = parseNodePtyDiagnostic(message)
|
||||
if (!diagnostic) {
|
||||
if (GENERIC_PTY_ALLOCATION_RE.test(message) && !message.startsWith(PTY_ALLOCATION_HINT)) {
|
||||
return `${PTY_ALLOCATION_HINT} ${message}`
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
const hint = getNodePtyRecoveryHint(diagnostic)
|
||||
if (hint && message.startsWith(hint)) {
|
||||
return message
|
||||
}
|
||||
return hint ? `${hint} ${message}` : message
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user