Eugeny/tabby · error · Error
${method} ${url} failed: ${response.status} ${response.statu
Error message
${method} ${url} failed: ${response.status} ${response.statusText} What it means
Thrown by `ConfigSyncService`'s fetch helper when the remote responds with a non-2xx status (`response.ok === false`). The message includes the HTTP method, full URL, status code, and status text to aid diagnosis. It is re-thrown after being logged so callers can handle transport/server failures.
Source
Thrown at tabby-settings/src/services/configSync.service.ts:188
// arbitrary command execution on the next sync. Require HTTPS.
if (!/^https:\/\//i.test(host)) {
const message = `Config sync host must use HTTPS (got: ${host})`
this.logger.error(message)
throw new Error(message)
}
url = host + url
this.logger.debug(`${method} ${url}`, data)
try {
const response = await fetch(url, {
method,
headers: {
Authorization: `Bearer ${this.config.store.configSync.token}`,
...data !== undefined ? { 'Content-Type': 'application/json' } : {},
},
body: data !== undefined ? JSON.stringify(data) : undefined,
})
if (!response.ok) {
throw new Error(`${method} ${url} failed: ${response.status} ${response.statusText}`)
}
this.logger.debug(response)
// ponytail: DELETE returns empty 204, parse only if there's a body
const text = await response.text()
return text ? JSON.parse(text) : undefined
} catch (error) {
this.logger.error(error)
throw error
}
}
private async autoSync () {
while (true) {
try {
if (this.isEnabled() && this.config.store.configSync.auto) {
const cfg = await this.getConfig(this.config.store.configSync.configID)
if (new Date(cfg.modified_at) > this.lastRemoteChange) {
this.logger.info('Remote config changed, downloading')View on GitHub (pinned to 14e2d60b9b)
Solutions
- Inspect the status code in the message: 401/403 -> fix the bearer token in `configSync.token`; 404 -> verify host/path; 5xx -> server-side issue, retry with backoff.
- Refresh the access token and retry the sync operation.
- Confirm the sync server endpoint matches what this Tabby version expects (API version drift).
- Wrap sync calls in try/catch, surface the status to the user, and offer a retry with exponential backoff for transient 5xx/429.
Example fix
// before
if (!response.ok) throw new Error(`${method} ${url} failed: ${response.status} ${response.statusText}`)
// after - structured error + retry for transient failures
if (!response.ok) {
const err = new Error(`${method} ${url} failed: ${response.status} ${response.statusText}`)
;(err as any).status = response.status
;(err as any).transient = response.status >= 500 || response.status === 429
throw err
} Defensive patterns
Strategy: retry
Validate before calling
function isTransientStatus (status: number): boolean {
return status >= 500 || status === 429
}
// pre-flight: refresh token if known to be near expiry
if (tokenExpired(config.store.configSync.token)) {
await refreshToken()
} Type guard
function isHttpError (e: unknown): e is Error & { status?: number } {
return e instanceof Error && typeof (e as any).status === 'number'
} Try / catch
async function syncWithRetry (fn: () => Promise<any>, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try { return await fn() }
catch (e) {
const status = (e as any)?.status ?? parseInt(/failed: (\d+)/.exec(String(e))?.[1] ?? '0', 10)
if (!isTransientStatus(status) || i === attempts - 1) throw e
await new Promise(r => setTimeout(r, 2 ** i * 500))
}
}
} Prevention
- Keep the configSync token fresh; refresh before expiry.
- Confirm the sync server endpoint version matches this Tabby build.
- Surface the status code to the user so 401 vs 404 vs 500 is actionable.
- Retry only transient (5xx/429) failures with exponential backoff; do not retry 4xx.
When it happens
Trigger: Any sync HTTP call (GET/POST/PUT/DELETE against the configured host) returning 4xx or 5xx: 401 (bad/expired token), 403 (forbidden), 404 (wrong endpoint/host), 409 (conflict), 500 (server error), 502/503 (gateway).
Common situations: Expired or wrong `configSync.token` (401); host reachable but path wrong (404); server-side bug during merge (500); rate limiting (429); network proxy rewriting responses.
Related errors
- Config sync host must use HTTPS (got: ${host})
- Remote rejected opening a shell channel: ${err}
- Authentication rejected
- No username
AI-assisted analysis of Eugeny/tabby@14e2d60b9b (2026-08-12).
Data as JSON: /api/errors/613d10e972531434.
Report an issue: GitHub.