How do I solve this 401 error whenever I run my firebase functions?

I dont have any software experience, I just started using ai to build something for myself in late November. I’m working with a react web app and I’m trying to deploy in production. I’m trying to run my firebase functions, but im getting this error:

Failed to load resource: the server responded with a status of 401 ()
LGChat.js:78 Full error object: FirebaseError: The function must be called while authenticated.

it is telling me i need to be authenticated to do it. however, i am fully authenticated. ive logged in, and firebase storage is pulling the documents associated with the account. so i know that other firebase services are working. ive deleted the account and remade it, so i know auth isnt broken. and the way callable functions work from my understanding is firebase does the token work on their end. so nothing i add to my code has influenced the the token being sent.

Every time i try to use the function, the token is sent, and then firebase denies it. I can’t imagine that the token is incorrect. Its the one firebase is generating and pulling itself from its own auth.

The llms have suggested mostly environment issues. I’ve checked every one they’ve suggested. I’ve also confirmed that I am fully signed into the google cloud sdk.

Does anyone have any experience that could point me in a new direction to solve this? it seems like such a dumb problem lol.

The error message is being called from in my functions/index.js and in a file called LGChat. Please let me know what else I can send to be helpful. I’m new to this so feel free to assume I don’t know what you are talking about. Also, sorry if I’ve structured this stackoverflow post incorrectly.

I’ve carefully gone through all of the potential environment issues.

I confirmed I’m logged into google cloud.

I’ve gone through the callables documentation to confirm everything is set up correctly.

I’ve confirmed my gcloud secret, etc it all correct.

I’ve confirmed the other firebase services are working.

I’ve given all users have invoker access.

I clear my cache on every redeployment attempt.

What am I missing?

Thank you in advance for any help.

Screenshot of error messages

LGChat file:


  const processDocument = async () => {
    if (!selectedFile || isProcessing || isProcessed || !currentUser) return;

    setIsProcessing(true);
    setError(null);
    setProcessingStatus('Starting processing...');

    try {
      // Use the already initialized functions instance from above
      const processPdfDocument = httpsCallable(functions, 'processPdfDocument', {
        timeout: 540000
      });

      console.log('Processing document with auth:', {
        uid: currentUser?.uid,
        email: currentUser?.email
      });

      console.log('Document details being sent:', {
        fileId: selectedFile.id,
        fileName: selectedFile.name,
        fileUrl: selectedFile.url,
        userId: currentUser.uid
      });

      setProcessingStatus('Processing PDF...');
      console.log('Processing PDF...');
      const result = await processPdfDocument({
        fileId: selectedFile.id,
        fileName: selectedFile.name,
        fileUrl: selectedFile.url,
        userId: currentUser.uid
      });
      console.log('Processing PDF result:', result);

      if (result.data?.success) {
        setIsProcessed(true);
        setProcessingStatus(
          `Successfully processed ${result.data.pagesProcessed} pages`
        );
      } else {
        throw new Error(
          'Processing failed: ' + (result.data?.error || 'Unknown error')
        );
      }
    } catch (error) {
      console.error('Error processing document:', error);
      setError(error.message || 'Failed to process document.');
      setIsProcessed(false);
      setProcessingStatus('Processing failed');
    }
  };

functions/index.js:

// Process PDF
exports.processPdfDocument = onCall(
  {
    memory: "2GiB",
    timeoutSeconds: 540,
    cors: true,
    enforceAppCheck: false,
    secrets: [
      "OPENAI_API_KEY",
      "PINECONE_API_KEY",
      "PINECONE_INDEX_NAME",
      "PINECONE_HOST"
    ]
  }, 
  async (request, context) => {
    // Log the full context and request for debugging
    console.log("[processPdfDocument] Request data:", {
      auth: request.auth,
      rawRequest: request.rawRequest,
      data: request.data
    });

    console.log("[processPdfDocument] Context:", {
      auth: context.auth,
      rawRequest: context.rawRequest
    });

    // Check auth
    if (!request.auth) {
      console.error("[processPdfDocument] No auth context");
      throw new HttpsError(
        "unauthenticated",
        "The function must be called while authenticated."
      );
    }

    // Verify UID
    if (!request.auth.uid) {
      console.error("[processPdfDocument] No UID in auth context");
      throw new HttpsError(
        "unauthenticated",
        "Invalid authentication. Please sign in again."
      );
    }

    try {
      // Verify the token
      const decodedToken = await admin.auth().verifyIdToken(context.auth.token);
      console.log("[processPdfDocument] Token verified:", decodedToken.uid);

      console.log("[processPdfDocument] Auth context:", {
        hasAuth: Boolean(context.auth),
        uid: context.auth ? context.auth.uid : null,
        token: context.auth ? Boolean(context.auth.token) : false,
        app: Boolean(context.app)
      });

      if (!request.auth || !request.auth.uid) {
        console.error("[processPdfDocument] Authentication error: No valid auth context");
        throw new HttpsError(
          "unauthenticated",
          "The function must be called while authenticated."
        );
      }

      try {
        console.log("[processPdfDocument] Request data:", {
          ...request.data,
          auth: {uid: context.auth.uid}
        });

        const {fileId, fileName, path} = request.data;
        const uid = context.auth.uid;

        // Validate parameters with detailed logging
        const missingParams = [];
        if (!fileId) missingParams.push("fileId");
        if (!fileName) missingParams.push("fileName");
        if (!path) missingParams.push("path");

        if (missingParams.length > 0) {
          const errorMsg = `Missing required parameters: ${missingParams.join(", ")}`;
          console.error("[processPdfDocument] Parameter validation failed:", {
            received: {fileId, fileName, path},
            missing: missingParams
          });
          throw new HttpsError("invalid-argument", errorMsg);
        }

        // Validate config with error handling
        let config;
        try {
          config = getConfig();
          console.log("[processPdfDocument] Configuration validated successfully");
        } catch (configError) {
          console.error("[processPdfDocument] Configuration error:", configError);
          throw new HttpsError(
            "failed-precondition",
            `Configuration error: ${configError.message}`
          );
        }

        // Initialize storage and get file
        const storage = getStorage();
        const bucket = storage.bucket();
        const tempFilePath = `/tmp/${fileId}-${Date.now()}.pdf`;

        // Download file with detailed error handling
        try {
          console.log("[processPdfDocument] Attempting to download file:", {path, tempFilePath});
          await bucket.file(path).download({destination: tempFilePath});
          console.log("[processPdfDocument] File downloaded successfully");
        } catch (downloadError) {
          console.error("[processPdfDocument] Download error:", {
            error: downloadError,
            path,
            tempFilePath
          });
          throw new HttpsError(
            "internal",
            `Failed to download file: ${downloadError.message}`
          );
        }

        // Process PDF with error handling
        let pdfContent;
        try {
          const dataBuffer = await fs.readFile(tempFilePath);
          console.log("[processPdfDocument] File read successfully, size:", dataBuffer.length);
          
          pdfContent = await pdf(dataBuffer, {
            pageNumbers: true,
            normalizeWhitespace: true,
            disableCombineTextItems: false
          });
          console.log("[processPdfDocument] PDF parsed successfully, pages:", pdfContent.numpages);
        } catch (pdfError) {
          console.error("[processPdfDocument] PDF processing error:", pdfError);
          throw new HttpsError(
            "internal",
            `Failed to process PDF: ${pdfError.message}`
          );
        }

        // Create text chunks
        const splitter = new RecursiveCharacterTextSplitter({
          chunkSize: 1000,
          chunkOverlap: 200
        });

        let allDocs = [];
        try {
          const docs = await splitter.createDocuments(
            [pdfContent.text],
            [{
              pageNumber: 1,
              fileId,
              fileName,
              userId: uid
            }]
          );
          allDocs = docs;
          console.log("[processPdfDocument] Created chunks:", allDocs.length);
        } catch (splitError) {
          console.error("[processPdfDocument] Text splitting error:", splitError);
          throw new HttpsError(
            "internal",
            `Failed to split text: ${splitError.message}`
          );
        }

        // Initialize Pinecone with error handling
        let pineconeIndex;
        try {
          const pineconeOptions = {
            apiKey: config.pineconeApiKey
          };
          if (config.pineconeHost) {
            pineconeOptions.controllerHostUrl = config.pineconeHost;
          }
          const pinecone = new Pinecone(pineconeOptions);
          pineconeIndex = pinecone.index(config.pineconeIndexName);
          console.log("[processPdfDocument] Pinecone initialized successfully");
        } catch (pineconeError) {
          console.error("[processPdfDocument] Pinecone initialization error:", pineconeError);
          throw new HttpsError(
            "internal",
            `Failed to initialize Pinecone: ${pineconeError.message}`
          );
        }

        // Create and store embeddings
        try {
          const embeddings = new OpenAIEmbeddings({
            openAIApiKey: config.openaiApiKey,
            batchSize: 100
          });

          await PineconeStore.fromDocuments(allDocs, embeddings, {
            pineconeIndex,
            namespace: uid,
            maxConcurrency: 5
          });
          console.log("[processPdfDocument] Documents stored in Pinecone successfully");
        } catch (embeddingError) {
          console.error("[processPdfDocument] Embedding/storage error:", embeddingError);
          throw new HttpsError(
            "internal",
            `Failed to create/store embeddings: ${embeddingError.message}`
          );
        }

        // Cleanup temp files
        try {
          await fs.unlink(tempFilePath);
          console.log("[processPdfDocument] Cleaned up temporary file");
        } catch (cleanupError) {
          console.warn("[processPdfDocument] Cleanup warning:", cleanupError);
          // Don't throw on cleanup errors
        }

        return {
          success: true,
          chunksProcessed: allDocs.length,
          pagesProcessed: pdfContent.numpages || 1
        };

      } catch (error) {
        console.error("[processPdfDocument] Top-level error:", {
          message: error.message,
          code: error.code,
          stack: error.stack
        });
        
        if (error instanceof HttpsError) {
          throw error;
        }
        
        throw new HttpsError(
          "internal",
          `Processing failed: ${error.message}`
        );
      }
    } catch (error) {
      console.error("[processPdfDocument] Token verification failed:", error);
      throw new HttpsError(
        "unauthenticated",
        "Invalid authentication token. Please sign in again."
      );
    }
  }
);