INVALID_TARGET_URL and 400 errors when calling Bitbucket API in headless endpoint webtrigger function

I'm developing a headless Forge app that serves as a serverless endpoint to interact with the Bitbucket API. The app's purpose is to update design tokens in a specific repository. It doesn't have a UI component; instead, it's designed to be triggered externally and perform operations directly on the repository.

Key characteristics of my app:

  1. It's a headless Forge app with no UI.
  2. It uses a web trigger to create an endpoint that can be called externally.
  3. Its main function is to update a JSON file containing design tokens in a Bitbucket repository.
  4. It needs to read the current file (if it exists), update its contents, and commit the changes back to the repository.

I'm encountering persistent issues with my Bitbucket API calls, receiving both INVALID_TARGET_URL and 400 errors. Here's my current setup:


import api, { route } from "@forge/api";

const WORKSPACE = 'rcapeteam';
const REPO_SLUG = 'style-dictionary';
const BRANCH = 'main';
const FILE_PATH = 'input/design-tokens.json';

export const designTokensHandler = async (req, context) => {
  // ... (request parsing code) ...

  try {
    // Get the current file (if it exists)
    const getFileUrl = route`/2.0/repositories/${WORKSPACE}/${REPO_SLUG}/src/${BRANCH}/${FILE_PATH}`;
    let currentFile;
    try {
      const response = await api.asApp().requestBitbucket(getFileUrl);
      if (response.status === 200) {
        currentFile = await response.json();
      }
    } catch (error) {
      console.log('Error fetching current file:', error);
      if (error.response && error.response.status !== 404) throw error;
    }

    // Prepare the commit
    const message = commitMessage || 'Update design tokens';

    // Create or update the file
    const updateFileUrl = route`/2.0/repositories/${WORKSPACE}/${REPO_SLUG}/src`;
    const response = await api.asApp().requestBitbucket(
      updateFileUrl,
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          message: message,
          branch: BRANCH,
          files: [
            {
              path: FILE_PATH,
              contents: JSON.stringify(parsedTokens, null, 2)
            }
          ],
          ...(currentFile && { parents: [currentFile.commit.hash] })
        })
      }
    );

    if (!response.ok) {
      const errorBody = await response.text();
      throw new Error(`HTTP error! status: ${response.status}, body: ${errorBody}`);
    }

    // ... (success response handling) ...

  } catch (error) {
    console.error('Error updating design tokens:', error);
    // ... (error response handling) ...
  }
};

My manifest.yml includes:


modules:
  webtrigger:
    - key: design-tokens-trigger
      function: designTokensHandler

permissions:
  scopes:
    - read:repository:bitbucket
    - write:repository:bitbucket

I've tried:

  1. Hardcoding the URLs
  2. Using the route template literal
  3. Checking and double-checking permissions
  4. Verifying the Bitbucket API documentation

Despite these attempts, I'm still receiving errors. Initially, I got "Disallowing path manipulation attempt" errors. After addressing those, I'm now getting 400 Bad Request errors.

Questions:

  1. What am I missing in my Bitbucket API calls for this type of headless, serverless Forge app?
  2. Are there any known issues with Forge apps interacting with the Bitbucket API in this way?
  3. How can I debug this further? Are there any Forge-specific tools or logs I should be looking at for a headless app like this?
  4. Is there anything specific I need to consider when creating a headless, endpoint-only Forge app that interacts with Bitbucket?

Any help or guidance would be greatly appreciated. Thank you!