Adding view.getContext causes logging to stop working

I have a bitbucket dynamic provider app that I have created. I want to add support for licensing to it, so I’m following https://developer.atlassian.com/platform/marketplace/listing-forge-apps/#building-a-paid-app.

I have a number of console.log statements in my code so I can see what the app is doing when running forge tunnel. As soon as I add a call to view.getContext() in my app, my console.log messages stop working in forge tunnel and all I can see are the invocation: xxxxxxxxxxx index.main messages.

Is this a know bug?

To clarify what code I’m adding that stops the logging is:

export const isLicensed = async () => {
  const context = await view.getContext();
  return !!context.license?.active;
};

Simply adding that function to my index.ts (with no calls to that function) immediately stop the console.log output.

I’m using:

  • typescript 5.8.3
  • nodejs22.x

Those logs should be showing up in the frontend, ie in the browser console.

Also the license object only exists in paid apps in production. They have a convoluted way to test that: https://developer.atlassian.com/platform/marketplace/listing-forge-apps/#testing-your-app-with-different-license-states

@nathanwaters Thank you for your response.
I have tried by exporting an environment variable of FORGE_USER_VAR_LICENSE_OVERRIDE=inactive and also with FORGE_USER_VAR_LICENSE_OVERRIDE=active. It doesn’t change the behavior I’m seeing.

For backend code, all of my console.log statements work fine when running forge tunnel. I can see the log output in my terminal for all of the console.log statements; however, as soon as I add a call to view.getContext(), all console.log statements stop working.

I don’t even have to call view.getContext() for logging to stop working. Simply having it in a function, even if that function isn’t called, will stop log statements.
For example, if I add the following, logging will stop working, even if I don’t call this function:

import { view } from '@forge/bridge';
const isLicensed = async (): Promise<boolean> => {
  const context = await view.getContext();
  return !!context.license?.active;
};

It almost seems like Forge is checking if viewContext is in the code anywhere and disables logging if it is.

Actually, I just noticed it makes the app stop working. I just forked the hello-world example app, added an unused isLicensed function to index.js and it broke the app in the same way.
Is there something I’m just fundamentally doing wrong here?

Adding this line breaks the app.
That function is not even called and it breaks the app.

Hmm two things I can see…

You probably don’t want to be using @forge/bridge inside src/index.js. That’s a frontend package. Instead move that logic to src/frontend/index.jsx

I’ve also had issues in the past using useEffect(async () => { ... }, []). The Forge bundler can’t handle it. So instead do something like:

  useEffect(() => {
    const doStuff = async () => {
      const context = await view.getContext();
      console.log(context); // this is in browser devtools console
    };
    doStuff();
  }, []);

@nathanwaters Thank you. Moving that code to the frontend fixed the problem with it breaking the app.
Unfortunately, I still can’t seem to get licensing working in development environment. I’ve tried everything the page suggested:

  • Installed the app with: forge install -e development -p bitbucket --license active
  • Set forge variable with: forge variables set -e development LICENSE_OVERRIDE active
  • Set environment variable before running forge tunnel: export FORGE_USER_VAR_LICENSE_OVERRIDE=active

The console.log on the frontend just shows license: null and the console.log on the backend just shows license: undefined.

Does anyone know if there is an example app showing how to properly use licensing? I’ve been unable to find one so far.

Thanks for all of the help so far.

Yeah Atlassian isn’t very good at writing docs. I think this line means that you can’t test license variables in the frontend while tunneling:

Please make sure you check the value of context in an invocation context, because environment variables and the context object are not accessible during the snapshot context.

I’d skip the forge/env variables and tunneling:

import React, { useEffect, useState } from "react";
import ForgeReconciler, { Text } from "@forge/react";
import { view } from "@forge/bridge";

const App = () => {
  const [licensed, setLicensed] = useState(false);

  useEffect(() => {
    const handleLicense = async () => {
      const context = await view.getContext();
      if (context.license.active) setLicensed(true);
    };
    handleLicense();
  }, []);

  if (!licensed) return <Text>Not licensed</Text>; // show nice UI here

  return (
    <>
      <Text>Rest of the app</Text>
    </>
  );
};

ForgeReconciler.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Then:

  1. forge deploy and forge install
  2. Test without tunneling, then uninstall app
  3. forge install --license active
  4. Test without tunneling, then uninstall app
  5. forge install --license inactive
  6. Test without tunneling

@JohnAlberts1 I see the same behaviour with a Bitbucket app.

Note that currently paid Forge apps can’t be distributed through the Marketplace (free apps can currently be distributed via a distribution link workaround), so I assume the issue is related to Atlassian not having enable licensing for Bitbucket apps. There’s a relevant issue tracking this here.

Thank you both @nathanwaters and @joshp. Unfortunately, the answer to my problem appears to be what @joshp said, which is, Bitbucket apps are not supported… even though there is no mention of this at all in any of the documentation.
I’ve voted on the issue and added a comment.

If anyone else reading this would like to vote and add a comment to add Marketplace and licensing support for Bitbucket: