Files
stack/docs/scratchpads/275-silent-connection-failures.md
Jason Woltje 7d9c102c6d
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
fix(#275): Prevent silent connection initiation failures
Fixed silent connection initiation failures where HTTP errors were caught
but success was returned to the user, leaving zombie connections in
PENDING state forever.

Changes:
- Delete failed connection from database when HTTP request fails
- Throw BadRequestException with clear error message
- Added test to verify connection deletion and exception throwing
- Import BadRequestException in connection.service.ts

User Impact:
- Users now receive immediate feedback when connection initiation fails
- No more zombie connections stuck in PENDING state
- Clear error messages indicate the reason for failure

Testing:
- Added test case: "should delete connection and throw error if request fails"
- All 21 connection service tests passing
- Quality gates: lint, typecheck, build all passing

Fixes #275

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 20:21:06 -06:00

2.2 KiB

Issue #275: Fix silent connection initiation failures

Objective

Fix silent connection initiation failures where HTTP errors are caught but success is returned to the user, leaving zombie connections in PENDING state forever.

Location

apps/api/src/federation/connection.service.ts:72-80

Problem

Current code:

try {
  await firstValueFrom(
    this.httpService.post(`${remoteUrl}/api/v1/federation/incoming/connect`, signedRequest)
  );
  this.logger.log(`Connection request sent to ${remoteUrl}`);
} catch (error) {
  this.logger.error(`Failed to send connection request to ${remoteUrl}`, error);
  // Connection is still created in PENDING state, can be retried
}

return this.mapToConnectionDetails(connection);

Issues:

  • Catches HTTP failures but returns success
  • Connection stays in PENDING state forever
  • Creates zombie connections
  • User sees success message but connection actually failed

Solution

  1. Delete the failed connection from database
  2. Throw exception with clear error message
  3. User gets immediate feedback that connection failed

Implementation

try {
  await firstValueFrom(
    this.httpService.post(`${remoteUrl}/api/v1/federation/incoming/connect`, signedRequest)
  );
  this.logger.log(`Connection request sent to ${remoteUrl}`);
} catch (error) {
  this.logger.error(`Failed to send connection request to ${remoteUrl}`, error);

  // Delete the failed connection to prevent zombie connections
  await this.prisma.federationConnection.delete({
    where: { id: connection.id },
  });

  throw new BadRequestException(
    `Failed to initiate connection to ${remoteUrl}: ${error instanceof Error ? error.message : "Unknown error"}`
  );
}

Testing

Test scenarios:

  1. Remote instance is unreachable - should throw exception and delete connection
  2. Remote instance returns error - should throw exception and delete connection
  3. Remote instance times out - should throw exception and delete connection
  4. Remote instance returns success - should create connection in PENDING state

Progress

  • Create scratchpad
  • Implement fix in connection.service.ts
  • Add/update tests
  • Run quality gates
  • Commit changes
  • Create PR
  • Merge to develop
  • Close issue #275