Skip to main content

Documentation Index

Fetch the complete documentation index at: https://dylankenneally-react-native-ssh-sftp-96.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Promise Rejection Patterns

All asynchronous operations in the React Native SSH SFTP library return Promises that reject when errors occur. This provides a consistent error handling pattern across the API.

Basic Error Handling

Use try/catch blocks with async/await:
try {
  const client = await SSHClient.connectWithPassword(
    '10.0.0.10',
    22,
    'user',
    'password'
  );
  const output = await client.execute('ls -la');
  console.log(output);
} catch (error) {
  console.error('Operation failed:', error);
}
Or use .catch() with Promises:
SSHClient.connectWithPassword('10.0.0.10', 22, 'user', 'password')
  .then(client => client.execute('ls -la'))
  .then(output => console.log(output))
  .catch(error => console.error('Operation failed:', error));

Error Types

The library uses a generic error type from the native modules:
type CBError = any; // eslint-disable-line @typescript-eslint/no-explicit-any
Errors can originate from:
  1. Connection errors: Network issues, authentication failures, timeouts
  2. SFTP errors: File not found, permission denied, disk full
  3. SSH command errors: Command execution failures, shell errors
  4. Native module errors: Platform-specific errors from iOS/Android implementations

Common Errors

Authentication Errors

Occur when credentials are invalid or key-based authentication fails:
try {
  const client = await SSHClient.connectWithPassword(
    host,
    port,
    'wronguser',
    'wrongpass'
  );
} catch (error) {
  // Common authentication errors:
  // - Invalid credentials
  // - Account locked
  // - SSH service not available
  console.error('Authentication failed:', error);
}

Connection Errors

Occur when the client cannot establish a connection:
try {
  const client = await SSHClient.connectWithKey(
    'unreachable-host.example.com',
    22,
    'user',
    privateKey
  );
} catch (error) {
  // Common connection errors:
  // - Host unreachable
  // - Connection timeout
  // - Connection refused
  // - Network unreachable
  console.error('Connection failed:', error);
}

SFTP Errors

Occur during file transfer operations:
try {
  await client.sftpDownload('/nonexistent/file.txt', '/local/path/');
} catch (error) {
  // Common SFTP errors:
  // - File not found
  // - Permission denied
  // - Disk full
  // - Directory doesn't exist
  console.error('SFTP operation failed:', error);
}

Command Execution Errors

Occur when SSH commands fail:
try {
  const output = await client.execute('invalid-command');
} catch (error) {
  // Common command errors:
  // - Command not found
  // - Permission denied
  // - Command returned non-zero exit code
  console.error('Command execution failed:', error);
}

Error Handling Best Practices

1. Always Handle Errors

Never ignore Promise rejections. Always implement error handling for all asynchronous operations.
// Bad: No error handling
const client = await SSHClient.connectWithPassword(host, port, user, pass);
const output = await client.execute('rm -rf /');

// Good: Comprehensive error handling
try {
  const client = await SSHClient.connectWithPassword(host, port, user, pass);
  const output = await client.execute('ls');
  console.log(output);
} catch (error) {
  console.error('Operation failed:', error);
  // Implement recovery logic
}

2. Clean Up Resources on Error

Always disconnect clients, even when errors occur:
let client;
try {
  client = await SSHClient.connectWithPassword(host, port, username, password);
  await client.execute('some-command');
  await client.sftpUpload(localFile, remoteFile);
} catch (error) {
  console.error('Error during operations:', error);
  // Handle error
} finally {
  // Always cleanup
  if (client) {
    client.disconnect();
  }
}

3. Separate Connection and Operation Errors

Handle connection errors separately from operation errors:
let client;

try {
  // Connection phase
  client = await SSHClient.connectWithPassword(host, port, username, password);
} catch (error) {
  console.error('Failed to connect:', error);
  // Show connection error to user
  return;
}

try {
  // Operation phase
  const files = await client.sftpLs('/home/user');
  await client.sftpDownload('/home/user/file.txt', '/local/path/');
} catch (error) {
  console.error('Operation failed:', error);
  // Show operation error to user
} finally {
  client.disconnect();
}

4. Provide User-Friendly Error Messages

Translate technical errors into user-friendly messages:
function getErrorMessage(error: any): string {
  const errorStr = String(error).toLowerCase();

  if (errorStr.includes('authentication')) {
    return 'Invalid username or password';
  }
  if (errorStr.includes('timeout') || errorStr.includes('unreachable')) {
    return 'Cannot reach server. Check your connection.';
  }
  if (errorStr.includes('permission')) {
    return 'Permission denied. Check file permissions.';
  }
  if (errorStr.includes('not found')) {
    return 'File or directory not found';
  }

  return 'An unexpected error occurred. Please try again.';
}

try {
  const client = await SSHClient.connectWithPassword(host, port, user, pass);
  await client.execute('ls');
} catch (error) {
  const message = getErrorMessage(error);
  Alert.alert('Error', message);
}

5. Implement Retry Logic

For transient errors, implement retry with exponential backoff:
async function connectWithRetry(
  host: string,
  port: number,
  username: string,
  password: string,
  maxRetries = 3
): Promise<SSHClient> {
  let lastError;

  for (let i = 0; i < maxRetries; i++) {
    try {
      return await SSHClient.connectWithPassword(host, port, username, password);
    } catch (error) {
      lastError = error;
      console.log(`Connection attempt ${i + 1} failed, retrying...`);

      // Exponential backoff: 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
    }
  }

  throw lastError;
}

// Usage
try {
  const client = await connectWithRetry(host, port, username, password);
} catch (error) {
  console.error('Failed after retries:', error);
}

6. Handle File Transfer Cancellation

Properly handle cancelled uploads and downloads:
const client = await SSHClient.connectWithPassword(host, port, username, password);

let uploadInProgress = false;

const startUpload = async () => {
  uploadInProgress = true;
  try {
    await client.sftpUpload(localFile, remoteFile);
    console.log('Upload completed');
  } catch (error) {
    if (uploadInProgress) {
      // Upload failed
      console.error('Upload failed:', error);
    } else {
      // Upload was cancelled
      console.log('Upload cancelled');
    }
  } finally {
    uploadInProgress = false;
  }
};

const cancelUpload = () => {
  uploadInProgress = false;
  client.sftpCancelUpload();
};

7. Log Errors for Debugging

Implement comprehensive error logging:
function logError(context: string, error: any) {
  console.error(`[${context}] Error:`, {
    message: error.message || String(error),
    stack: error.stack,
    timestamp: new Date().toISOString(),
  });
}

try {
  const client = await SSHClient.connectWithPassword(host, port, username, password);
  await client.execute('ls');
} catch (error) {
  logError('SSH Connection', error);
  throw error;
}

Using Callbacks (Alternative Pattern)

The library also supports callback-based error handling:
SSHClient.connectWithPassword(
  host,
  port,
  username,
  password,
  (error) => {
    if (error) {
      console.error('Connection failed:', error);
      return;
    }
    console.log('Connected successfully');
  }
).then(client => {
  // Use client
}).catch(error => {
  // Also handle Promise rejection
});
While callbacks are supported, using async/await with try/catch is the recommended approach for cleaner, more maintainable code.

React Native Specific Considerations

Network State

Check network connectivity before attempting connections:
import NetInfo from '@react-native-community/netinfo';

const state = await NetInfo.fetch();
if (!state.isConnected) {
  throw new Error('No network connection');
}

const client = await SSHClient.connectWithPassword(host, port, username, password);

Error Boundaries

Use React Error Boundaries to catch errors in components:
class SSHErrorBoundary extends React.Component {
  state = { hasError: false, error: null };

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    console.error('SSH Error:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <ErrorScreen error={this.state.error} />;
    }
    return this.props.children;
  }
}

Platform-Specific Errors

iOS Simulator Limitation

The library does not currently support the iOS simulator. Always test on physical iOS devices.
import { Platform } from 'react-native';

if (Platform.OS === 'ios' && __DEV__) {
  // Warn users in development
  console.warn('iOS simulator is not supported. Use a physical device.');
}

Next Steps

Last modified on March 26, 2026