infiniflow/ragflow · error · Error

Failed to fetch search detail

Error message

Failed to fetch search detail

What it means

ensure_oauth_token_dict expects either a full token dict (refresh_token+token) or a client config under the 'installed' or 'web' key. When neither is present there is nothing to mint tokens from, so it raises ValueError — the credential blob is structurally unusable.

Source

Thrown at web/src/pages/next-searches/hooks.ts:251

  const shared_id = searchParams.get('shared_id');
  const searchId = id || shared_id;

  const { data, isLoading, isError } = useQuery<SearchDetailResponse, Error>({
    queryKey: ['searchDetail', searchId],
    enabled: !shared_id || !!tenantId,
    queryFn: async () => {
      let res;
      if (shared_id) {
        res = await searchService.getSearchDetailShare(
          { params: { search_id: searchId, tenant_id: tenantId } },
          true,
        );
      } else {
        res = await searchService.getSearchDetail({ search_id: searchId });
      }
      const response = res.data;
      if (response.code !== 0) {
        throw new Error(response.message || 'Failed to fetch search detail');
      }
      return response;
    },
  });

  return { data: data?.data, isLoading, isError };
};

export const useDeleteSearch = () => {
  const { t } = useTranslation();
  const queryClient = useQueryClient();
  const {
    data,
    isError,
    mutateAsync: deleteSearchMutation,
  } = useMutation<DeleteSearchResponse, Error, DeleteSearchProps>({
    mutationKey: ['deleteSearch'],
    mutationFn: async (props) => {

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Use the correct credential field: service-account JSON goes in the service-account credential, not the OAuth token field
  2. If OAuth: paste the full client_secret.json (with the 'installed' key) or a complete token blob so the flow can regenerate tokens
  3. Validate the blob's shape before saving — must have refresh_token+token, or an 'installed'/'web' key

Example fix

# before: service-account key pasted as OAuth creds
oauth_blob = {"type": "service_account", "project_id": "acme"}

# after: proper OAuth client config wrapper
oauth_blob = {"installed": {"client_id": "...", "client_secret": "...",
                             "auth_uri": "...", "token_uri": "..."}}
Defensive patterns

Strategy: validation

Validate before calling

def oauth_credential_usable(creds: dict) -> bool:
    if "refresh_token" in creds and "token" in creds:
        return True
    return "installed" in creds or "web" in creds

if not oauth_credential_usable(blob):
    raise ValueError("Need refresh_token+token, or an 'installed'/'web' client config")

Type guard

def is_oauth_client_config(d: dict) -> bool:
    return "installed" in d or "web" in d

def is_full_token_dict(d: dict) -> bool:
    return "refresh_token" in d and "token" in d

Try / catch

try:
    enriched = ensure_oauth_token_dict(blob, source)
except ValueError as e:
    if "missing both tokens and a client configuration" in str(e):
        blob = normalize_pasted_json(blob)  # re-wrap under 'installed' if a client_secret was pasted
        enriched = ensure_oauth_token_dict(blob, source)

Prevention

When it happens

Trigger: Credentials dict contains only an access token, only a refresh_token without a client config, arbitrary keys (e.g. 'type'/'project_id' from a service-account JSON pasted into the OAuth field), or the 'installed'/'web' wrappers were stripped when the JSON was flattened.

Common situations: Pasting a service-account key where OAuth tokens belong, pasting only the refresh_token string, hand-editing the JSON and deleting wrapper keys.

Related errors


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