{"record":{"id":"de999a547b6cf8b3","repo":"infiniflow/ragflow","slug":"failed-to-fetch-search-detail","errorCode":null,"errorMessage":"Failed to fetch search detail","messagePattern":"Failed to fetch search detail","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/pages/next-searches/hooks.ts","lineNumber":251,"sourceCode":"  const shared_id = searchParams.get('shared_id');\n  const searchId = id || shared_id;\n\n  const { data, isLoading, isError } = useQuery<SearchDetailResponse, Error>({\n    queryKey: ['searchDetail', searchId],\n    enabled: !shared_id || !!tenantId,\n    queryFn: async () => {\n      let res;\n      if (shared_id) {\n        res = await searchService.getSearchDetailShare(\n          { params: { search_id: searchId, tenant_id: tenantId } },\n          true,\n        );\n      } else {\n        res = await searchService.getSearchDetail({ search_id: searchId });\n      }\n      const response = res.data;\n      if (response.code !== 0) {\n        throw new Error(response.message || 'Failed to fetch search detail');\n      }\n      return response;\n    },\n  });\n\n  return { data: data?.data, isLoading, isError };\n};\n\nexport const useDeleteSearch = () => {\n  const { t } = useTranslation();\n  const queryClient = useQueryClient();\n  const {\n    data,\n    isError,\n    mutateAsync: deleteSearchMutation,\n  } = useMutation<DeleteSearchResponse, Error, DeleteSearchProps>({\n    mutationKey: ['deleteSearch'],\n    mutationFn: async (props) => {","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/web/src/pages/next-searches/hooks.ts#L233-L269","documentation":"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.","triggerScenarios":"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.","commonSituations":"Pasting a service-account key where OAuth tokens belong, pasting only the refresh_token string, hand-editing the JSON and deleting wrapper keys.","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"],"exampleFix":"# before: service-account key pasted as OAuth creds\noauth_blob = {\"type\": \"service_account\", \"project_id\": \"acme\"}\n\n# after: proper OAuth client config wrapper\noauth_blob = {\"installed\": {\"client_id\": \"...\", \"client_secret\": \"...\",\n                             \"auth_uri\": \"...\", \"token_uri\": \"...\"}}","handlingStrategy":"validation","validationCode":"def oauth_credential_usable(creds: dict) -> bool:\n    if \"refresh_token\" in creds and \"token\" in creds:\n        return True\n    return \"installed\" in creds or \"web\" in creds\n\nif not oauth_credential_usable(blob):\n    raise ValueError(\"Need refresh_token+token, or an 'installed'/'web' client config\")","typeGuard":"def is_oauth_client_config(d: dict) -> bool:\n    return \"installed\" in d or \"web\" in d\n\ndef is_full_token_dict(d: dict) -> bool:\n    return \"refresh_token\" in d and \"token\" in d","tryCatchPattern":"try:\n    enriched = ensure_oauth_token_dict(blob, source)\nexcept ValueError as e:\n    if \"missing both tokens and a client configuration\" in str(e):\n        blob = normalize_pasted_json(blob)  # re-wrap under 'installed' if a client_secret was pasted\n        enriched = ensure_oauth_token_dict(blob, source)","preventionTips":["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"],"tags":["google-oauth","credentials","validation","configuration"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}