-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy patherrors.ts
More file actions
160 lines (139 loc) · 4.82 KB
/
Copy patherrors.ts
File metadata and controls
160 lines (139 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/**
* Error thrown when an agent with the same name already exists.
*/
export class AgentAlreadyExistsError extends Error {
constructor(agentName: string) {
super(`An agent named "${agentName}" already exists in the schema.`);
this.name = 'AgentAlreadyExistsError';
}
}
/**
* Converts an unknown error to a string message.
* Handles Error instances and other thrown values consistently.
*/
export function getErrorMessage(err: unknown): string {
return err instanceof Error ? err.message : String(err);
}
/**
* Checks if an error is an AWS access denied error.
* Returns true for AccessDeniedException or AccessDenied error codes.
*/
export function isAccessDeniedError(err: unknown): boolean {
if (!err || typeof err !== 'object') {
return false;
}
const name = (err as { name?: string }).name;
return name === 'AccessDeniedException' || name === 'AccessDenied';
}
/**
* AWS error codes that indicate expired or invalid credentials.
* These errors can be recovered by re-authenticating.
*/
const EXPIRED_TOKEN_ERROR_CODES = new Set([
'ExpiredToken',
'ExpiredTokenException',
'TokenRefreshRequired',
'CredentialsExpired',
'InvalidIdentityToken',
'UnauthorizedAccess',
// Note: AccessDenied and AccessDeniedException are intentionally NOT included here.
// These are authorization errors (wrong account, missing IAM permissions, etc.),
// not authentication/token expiration errors.
'InvalidClientTokenId',
'SignatureDoesNotMatch',
'RequestExpired',
]);
/**
* Checks if an error is due to missing AWS credentials (not configured at all).
* Returns true for errors that indicate no credentials are available.
*/
export function isNoCredentialsError(err: unknown): boolean {
if (!err || typeof err !== 'object') {
return false;
}
const error = err as Record<string, unknown>;
// Check for AwsCredentialsError from account.ts
if (error.name === 'AwsCredentialsError') {
return true;
}
// Check error message for "no credentials" patterns
const message = getErrorMessage(err).toLowerCase();
if (
message.includes('no aws credentials') ||
message.includes('could not load credentials') ||
message.includes('credentials not found')
) {
return true;
}
return false;
}
/**
* Checks if an error is related to expired or invalid AWS credentials.
* Returns true for errors that can be recovered by re-authenticating.
*/
export function isExpiredTokenError(err: unknown): boolean {
if (!err || typeof err !== 'object') {
return false;
}
const error = err as Record<string, unknown>;
// Check AWS SDK v3 error structure (name property)
if (typeof error.name === 'string' && EXPIRED_TOKEN_ERROR_CODES.has(error.name)) {
return true;
}
// Check AWS SDK error Code property
if (typeof error.Code === 'string' && EXPIRED_TOKEN_ERROR_CODES.has(error.Code)) {
return true;
}
// Check nested error (some AWS errors wrap the actual error)
if (error.cause && typeof error.cause === 'object') {
return isExpiredTokenError(error.cause);
}
// Check error message for specific expiration patterns
const message = getErrorMessage(err).toLowerCase();
if (
message.includes('expired token') ||
message.includes('token has expired') ||
message.includes('credentials have expired') ||
message.includes('security token included in the request is expired') ||
message.includes('the security token included in the request is invalid')
) {
return true;
}
return false;
}
/**
* Checks if an error indicates the CloudFormation stack is in a transitional state.
* These errors occur when trying to deploy to a stack that is currently being updated.
*/
export function isStackInProgressError(err: unknown): boolean {
const message = getErrorMessage(err).toLowerCase();
// CloudFormation error patterns for in-progress stacks
if (
(message.includes('is in') && message.includes('state and cannot be updated')) ||
message.includes('update_in_progress') ||
message.includes('create_in_progress') ||
message.includes('delete_in_progress') ||
message.includes('rollback_in_progress') ||
message.includes('stack is currently being updated')
) {
return true;
}
return false;
}
/**
* Checks if an error indicates a CloudFormation changeset operation is in progress.
* This typically occurs when multiple deploys race and one tries to create/delete
* a changeset while another operation is already using it.
*/
export function isChangesetInProgressError(err: unknown): boolean {
const message = getErrorMessage(err).toLowerCase();
// CloudFormation changeset conflict patterns
if (
message.includes('invalidchangesetstatus') ||
message.includes('changeset is currently in progress') ||
message.includes('an operation on this changeset is currently in progress')
) {
return true;
}
return false;
}