• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / storage / 30076416950

24 Jul 2026 07:43AM UTC coverage: 80.268% (-0.1%) from 80.395%
30076416950

Pull #1230

github

web-flow
Merge 71d199328 into e845f9989
Pull Request #1230: feat: Added separate Database application.

5862 of 7870 branches covered (74.49%)

Branch coverage included in aggregate %.

527 of 634 new or added lines in 22 files covered. (83.12%)

11235 of 13430 relevant lines covered (83.66%)

420.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.98
/src/internal/database/postgres/pool-errors.ts
1
import { DatabaseError, type Pool } from 'pg'
2

3
const disposeClientOnRelease = Symbol('disposeClientOnRelease')
133✔
4

5
type DisposableQueryError = Error & {
6
  [disposeClientOnRelease]?: true
7
}
8

9
export function shouldDisposeClient(error: unknown): boolean {
10
  return (
145✔
11
    error instanceof Error &&
424✔
12
    (error.name === 'AbortError' ||
13
      (error as DisposableQueryError)[disposeClientOnRelease] === true)
14
  )
15
}
16

17
export function markClientDisposable(error: unknown): void {
18
  if (error instanceof Error) {
8!
19
    // PgPoolExecutor.shouldDisposeClient reads this marker from the exact Error instance
20
    // thrown by runPgQuery. Do not wrap or replace the error before the pool release path.
21
    const disposableError = error as DisposableQueryError
8✔
22
    disposableError[disposeClientOnRelease] = true
8✔
23
  }
24
}
25

26
export function isConnectionStateError(error: unknown): boolean {
27
  if (!(error instanceof Error)) {
104✔
28
    return false
1✔
29
  }
30

31
  const code = (error as NodeJS.ErrnoException).code
103✔
32
  return (
103✔
33
    (typeof code === 'string' && code.startsWith('08')) ||
467✔
34
    error.message.startsWith('received invalid response:') ||
35
    error.message.startsWith('Received unexpected ') ||
36
    error.message.startsWith('Unknown authenticationOk message type')
37
  )
38
}
39

40
// Socket-level of a dead pooled connection can surface as a plain Error.
41
// No setup is run yet, so a fresh client can safely retry.
42
// Connection-establishment failures (ECONNREFUSED, connect timeouts) aren't retried.
43
export function isBrokenClientError(error: unknown): boolean {
44
  if (!(error instanceof Error) || error.name === 'AbortError') {
10✔
45
    return false
2✔
46
  }
47

48
  if ((error as DisposableQueryError)[disposeClientOnRelease] === true) {
8!
NEW
49
    return true
×
50
  }
51

52
  const code = (error as NodeJS.ErrnoException).code
8✔
53
  return (
8✔
54
    code === 'ECONNRESET' ||
26✔
55
    code === 'EPIPE' ||
56
    error.message === 'Connection terminated unexpectedly' ||
57
    error.message === 'Client has encountered a connection error and is not queryable'
58
  )
59
}
60

61
export function isConnectionTimeoutError(error: unknown): error is Error {
62
  return (
9✔
63
    error instanceof Error &&
35✔
64
    (error.message === 'timeout expired' ||
65
      error.message === 'timeout exceeded when trying to connect' ||
66
      error.message === 'Connection terminated due to connection timeout')
67
  )
68
}
69

70
export function isConnectionLimitError(error: unknown): boolean {
71
  // PgBouncer can report connection limits as 08P01 protocol_violation. That
72
  // intentionally overlaps isConnectionStateError so these failed clients are
73
  // retried and disposed instead of being returned to the pool.
74
  return (
11✔
75
    error instanceof DatabaseError &&
15!
76
    ((error.code === '08P01' && error.message.includes('no more connections allowed')) ||
77
      error.message.includes('Max client connections reached'))
78
  )
79
}
80

81
export function isRetryableTransactionSetupError(error: unknown): boolean {
82
  return (
16✔
83
    isConnectionStateError(error) || isConnectionLimitError(error) || isBrokenClientError(error)
37✔
84
  )
85
}
86

87
export type PoolErrorHandler = (error: Error) => void
88

89
export function attachPoolErrorHandler<T extends Pool>(pool: T, onError: PoolErrorHandler): T {
90
  pool.on('error', onError)
86✔
91
  return pool
86✔
92
}
93

94
class AbortError extends Error {
95
  readonly code = 'ABORT_ERR'
133✔
96

97
  constructor() {
98
    super('Query was aborted')
133✔
99
    this.name = 'AbortError'
133✔
100
  }
101
}
102
export const ABORT_ERROR = new AbortError()
133✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc