Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions api/server/services/Files/Code/crud.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ async function getCodeOutputDownloadStream(fileIdentifier, identity, req, route
}
}

/**
* A deployment that no longer serves a ref's profile rejects every retry the same
* way, so failing the delete would leave a record the user can never remove and an
* agent that aborts every code turn on it.
*/
const isProfileMismatch = (error) =>
error.response?.status === 409 && error.response?.data?.error === 'execution_profile_mismatch';

/**
* Deletes a file from the Code Environment server.
*
Expand Down Expand Up @@ -148,15 +156,18 @@ async function deleteCodeEnvFile(req, file) {
timeout: 15000,
});
} catch (error) {
if (error.response?.status !== 404) {
const profileMismatch = isProfileMismatch(error);
if (error.response?.status !== 404 && !profileMismatch) {
throw error;
}
/* Already gone. Logged rather than swallowed: a 404 from a
* misconfigured base URL is indistinguishable from one for an absent
* object, and this branch drops the file's record either way. */
logAxiosError({
error,
message: `Code environment object already absent: ${error.message}`,
message: profileMismatch
? `Code environment no longer serves the ${executionProfile} profile; dropping the unreachable object: ${error.message}`
: `Code environment object already absent: ${error.message}`,
});
}
}
Expand Down
27 changes: 27 additions & 0 deletions api/server/services/Files/Code/crud.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,33 @@ describe('Code CRUD', () => {
expect(mockAxios).toHaveBeenCalledTimes(1);
});

it('treats objects on a profile the deployment no longer serves as unreachable', async () => {
mockAxios.mockRejectedValue(
Object.assign(new Error('Request failed with status code 409'), {
response: {
status: 409,
data: {
error: 'execution_profile_mismatch',
expected_profile: 'default',
actual_profile: 'stateful',
},
},
}),
);

await expect(deleteCodeEnvFile(req, file)).resolves.toBeUndefined();
});

it('throws on other conflicts', async () => {
mockAxios.mockRejectedValue(
Object.assign(new Error('conflict'), {
response: { status: 409, data: { error: 'session_busy' } },
}),
);

await expect(deleteCodeEnvFile(req, file)).rejects.toThrow('conflict');
});

it('throws when code environment deletion fails', async () => {
mockAxios.mockRejectedValue(
Object.assign(new Error('unavailable'), { response: { status: 500 } }),
Expand Down
Loading