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!

I am also getting the same (or very similar issue) here.
I have a function that is triggered via a web trigger (it will actually be triggered via a schedule) and I get:

UnhandledPromiseRejection with reason: Forge platform failed to process runtime HTTP request - 400 - INVALID_TARGET_URL

My functions are super simple:


export async function handler(event) {
  const prs = await getRepositories('myexistingworkspace');
  console.log(prs);
}
async function getRepositories(workspace) {
  return api.asApp().requestBitbucket(
    route`/2.0/repositories/${workspace}/`
  )
  .then(response => response.json());
}

My manifest is:

app:
  runtime:
    name: nodejs20.x
  id: ari:cloud:ecosystem::app/xxxx
modules:
  scheduledTrigger:
    - key: scheduled-trigger
      function: trigger
      interval: hour
  webtrigger:
    - key: web-trigger
      function: trigger
  function:
    - key: trigger
      handler: index.handler
permissions:
  scopes:
    - 'read:repository:bitbucket'
    - 'read:pullrequest:bitbucket'
    - 'write:repository:bitbucket'
    - 'admin:repository:bitbucket'
    - 'write:pullrequest:bitbucket'
    - 'read:project:bitbucket'
    - 'read:workspace:bitbucket'
    - 'read:user:bitbucket'

I realise the permissions are broad but like @ClayBailey I have been trying everything to get over the INVALID_TARGET_URL

Hello @ClayBailey and @MarkDrew,

Sorry to hear you’ve been blocked by this. It appears when constructing paths for the requestBitbucket API you must use the workspace id and not the workspace slug. The workspace id should be available in the function context, or if you only care about a single workspace you could hardcode it similar to you have here.

Hope that helps,
Sam

1 Like

Hi @SamSmyth
That totally worked! Nice one! Thanks for that!