infiniflow/ragflow · error · Error

Failed to delete memory

Error message

Failed to delete memory

What it means

Service-account branch of get_google_creds: the key JSON parsed and creds were built, but after an attempted refresh the credentials still report invalid — Google is rejecting the service-account key itself (deleted key, wrong private key, malformed JSON).

Source

Thrown at web/src/pages/memories/hooks.ts:174

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

export const useDeleteMemory = () => {
  const { t } = useTranslation();
  const queryClient = useQueryClient();
  const {
    data,
    isError,
    mutateAsync: deleteMemoryMutation,
  } = useMutation<DeleteMemoryResponse, Error, DeleteMemoryProps>({
    mutationKey: ['deleteMemory'],
    mutationFn: async (props) => {
      const { data: response } = await memoryService.deleteMemory(
        props.memory_id,
      );
      if (response.code !== 0) {
        throw new Error(response.message || 'Failed to delete memory');
      }

      queryClient.invalidateQueries({ queryKey: ['memoryList'] });
      return response;
    },
    onSuccess: () => {
      message.success(t('message.deleted'));
    },
    onError: (error) => {
      message.error(t('message.error', { error: error.message }));
    },
  });

  const deleteMemory = useCallback(
    (props: DeleteMemoryProps) => {
      return deleteMemoryMutation(props);
    },
    [deleteMemoryMutation],

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Download a fresh JSON key from GCP > IAM & Admin > Service Accounts > Keys and replace the credential verbatim
  2. Diff the new/old JSON — the private_key must retain its literal \n escapes; validate with json.loads before saving
  3. Confirm the service account is enabled and the project's Drive API is on

Example fix

# before: key mangled by YAML templating (real newlines inside private_key)
sa = {"private_key": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END"}

# after: intact escaped key, verified parseable
import json
sa = json.loads(open('sa-key.json').read())  # use file bytes as-is
assert "\\n" in sa["private_key"]  # escaped newlines preserved
Defensive patterns

Strategy: try-catch

Validate before calling

import json

def service_account_key_wellformed(json_str: str) -> bool:
    try:
        k = json.loads(json_str)
    except json.JSONDecodeError:
        return False
    return (
        k.get("type") == "service_account"
        and k.get("private_key", "").startswith("-----BEGIN PRIVATE KEY-----")
        and "\\n" in k["private_key"]
    )

Try / catch

from google.oauth2 import service_account

try:
    sa = service_account.Credentials.from_service_account_info(
        json.loads(sa_json), scopes=SCOPES)
    sa.refresh(Request())          # force validation now, not mid-index
except Exception:
    raise SystemExit("Service-account key rejected — download a fresh JSON from GCP")

Prevention

When it happens

Trigger: ServiceAccountCredentials.from_service_account_info succeeds structurally but .refresh(Request()) fails (invalid_grant / invalid_signature) and .valid stays False: key deleted in GCP, private_key corrupted by copy-paste, or the JSON is from a different project.

Common situations: Key rotated in GCP but old JSON still configured, newlines in the private key mangled by templating/YAML, email field mismatch after project rename.

Related errors


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