Skip to content

Error Handling

The SDK surfaces two typed error classes for the most common failure modes. All other errors propagate as standard ConnectError instances.

import { Client, StaleHeadError, RepoBusyError } from "@githosted/sdk";
const client = new Client();
const repo = client.repo("my-project");
try {
const file = await repo.read("config.json");
await repo.write("config.json", newContent, {
message: "Update config",
expectedHead: file.headSha,
});
} catch (err) {
if (err instanceof StaleHeadError) {
console.log(`Branch moved to ${err.actualHead}`);
} else if (err instanceof RepoBusyError) {
console.log(`Repo busy: ${err.operation}`);
}
}

Thrown when another operation is actively mutating the repo. The repo service uses per-repo leasing — only one writer at a time.

PropertyTypeDescription
repoIdstringCanonical repo ID (rp_ prefix)
operationstringWhat the current holder is doing

Auto-retry: The SDK automatically retries RepoBusyError with exponential backoff (100ms base, 3 retries max) before surfacing the error to your code. If you see this error, all retries have been exhausted.

import { isRepoBusyError } from "@githosted/sdk";
try {
await repo.write("file.ts", content, { message: "update" });
} catch (err) {
if (isRepoBusyError(err)) {
// All 3 retries failed — the repo is under sustained write load
console.log(`Repo ${err.repoId} busy with: ${err.operation}`);
}
}

Thrown when expectedHead doesn’t match the current branch tip. This means another write landed between your read and write.

PropertyTypeDescription
repoIdstringCanonical repo ID
refstringBranch that was written to
expectedHeadstringThe SHA you sent
actualHeadstringThe current branch tip

Never auto-retried. You must decide how to handle the conflict — re-read and retry, merge, or abort.

import { isStaleHeadError } from "@githosted/sdk";
try {
await repo.write("file.ts", content, {
message: "update",
expectedHead: previousHeadSha,
});
} catch (err) {
if (isStaleHeadError(err)) {
// Re-read from actualHead and try again
const fresh = await repo.read("file.ts");
// ... merge or retry with fresh.headSha
}
}

Both error types are backed by protobuf error detail messages attached to Connect errors:

Connect CodeError DetailSDK Error Class
AbortedRepoBusyDetailRepoBusyError
FailedPreconditionStaleHeadDetailStaleHeadError

The SDK parses these from the Connect error details field. It never infers error types from message strings.