infiniflow/ragflow · error · Error

Failed to fetch: ${message}

Error message

Failed to fetch: ${message}

What it means

The fallback error in the upload modal's fetchGitDirectoryContents for any non-ok response other than 403 and 404. It surfaces the server's own message from the parsed JSON body, or the bare HTTP status when the body is not JSON. Typical statuses behind it: 401 (bad token), 400 (malformed ref), 5xx, or proxy errors.

Source

Thrown at web/src/pages/skills/components/upload-modal.tsx:392

      const response = await fetch(url, { headers });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        const message = errorData.message || `HTTP ${response.status}`;

        if (response.status === 403) {
          const limit = platform === 'github' ? '60' : '1000';
          throw new Error(
            `API rate limit exceeded. ${limit} requests/hour for unauthenticated requests.`,
          );
        }
        if (response.status === 404) {
          throw new Error(
            'Repository or path not found. Please check the URL and ensure the repository is public.',
          );
        }
        throw new Error(`Failed to fetch: ${message}`);
      }

      const items = await response.json();
      const files: GitFile[] = [];

      // Handle single file case
      if (!Array.isArray(items)) {
        if (items.type === 'file') {
          files.push({
            path: items.path,
            download_url: items.download_url,
            type: 'file',
            size: items.size,
          });
        }
        return files;
      }

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Read the embedded `message` in the error — GitHub 401 says 'Bad credentials', indicating the token is wrong/expired
  2. Re-generate or correct the token in the modal's token field and retry
  3. Retry after a short wait for 5xx/transient upstream failures
  4. Verify network egress to api.github.com / gitee.com is not blocked by proxy
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check the token shape before using it
function isValidGitToken(token?: string): boolean {
  return !token || (token.length >= 20 && /^[A-Za-z0-9_]+$/.test(token));
}
if (!isValidGitToken(gitToken)) showTokenWarning();

Type guard

function isGithubApiError(v: unknown): v is { message: string } {
  return typeof v === 'object' && v !== null && typeof (v as any).message === 'string';
}

Try / catch

try {
  const items = await fetchGitDirectoryContents(/* ... */);
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e);
  if (msg.includes('Bad credentials')) {
    setGitValidationMessage('Token is invalid or expired — re-enter it.');
    return;
  }
  if (/HTTP 5\d\d/.test(msg)) {
    await retryAfter(2000); // transient upstream error
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: A token is provided but invalid/expired (401 with GitHub's 'Bad credentials' message); the ref query param is URL-encoded incorrectly (400); GitHub/Gitee outage or upstream 5xx; corporate proxy intercepting the request.

Common situations: Revoked or mistyped personal access token; token lacking scopes; GitHub API secondary rate limiting returning unusual status codes; environment where api.github.com is proxied and returns non-JSON errors.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/a2d75801f7e4b7d1. Report an issue: GitHub.