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:
- Connection errors: Network issues, authentication failures, timeouts
- SFTP errors: File not found, permission denied, disk full
- SSH command errors: Command execution failures, shell errors
- 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;
}
}
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