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
- Use the correct credential field: service-account JSON goes in the service-account credential, not the OAuth token field
- If OAuth: paste the full client_secret.json (with the 'installed' key) or a complete token blob so the flow can regenerate tokens
- 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
- Never paste service-account JSON into OAuth fields; label credential inputs clearly
- Auto-detect pasted client_secret.json and re-wrap it as {'installed': ...} at save time
- Keep the full token blob intact — don't prune 'unused-looking' keys
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
- main() returned a non-JSON-serializable value.
- message.compileNotSupported
- Invalid SSH provider configuration.
- main() must be defined or exported.
- main() must return a value. Use null for an empty result.
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/de999a547b6cf8b3.
Report an issue: GitHub.