SillyTavern/SillyTavern · warning

Forbidden: No permission to list branches of global extensio

Error message

Forbidden: No permission to list branches of global extensions.

What it means

The /branches endpoint (extensions.js:229-231) checks admin privileges when the request body has global set to truthy. Non-admin users cannot list branches of extensions in the global directory. This is the same authorization pattern used by /install and /update.

Source

Thrown at src/endpoints/extensions.js:231

        return response.status(500).send('Internal Server Error. Check the server logs for more details.');
    }
});

router.post('/branches', async (request, response) => {
    try {
        if (typeof request.body.extensionName !== 'string') {
            return response.status(400).send('Bad Request: A valid extensionName is required in the request body.');
        }

        const { extensionName, global } = request.body;
        const extensionNameSanitized = sanitize(extensionName);
        if (!extensionNameSanitized) {
            return response.status(400).send('Bad Request: A valid extensionName is required in the request body.');
        }

        if (global && !request.user.profile.admin) {
            console.error(`User ${request.user.profile.handle} does not have permission to list branches of global extensions.`);
            return response.status(403).send('Forbidden: No permission to list branches of global extensions.');
        }

        const basePath = global ? PUBLIC_DIRECTORIES.globalExtensions : request.user.directories.extensions;
        const extensionPath = path.join(basePath, extensionNameSanitized);

        if (!fs.existsSync(extensionPath)) {
            return response.status(404).send(`Directory does not exist at ${extensionPath}`);
        }

        const git = simpleGit({ baseDir: extensionPath, ...OPTIONS });
        // Unshallow the repository if it is shallow
        const isShallow = await git.revparse(['--is-shallow-repository']) === 'true';
        if (isShallow) {
            console.info(`Unshallowing the repository at ${extensionPath}`);
            await git.fetch('origin', ['--unshallow']);
        }

        // Fetch all branches

View on GitHub (pinned to 8172dcd0ee)

Solutions

  1. Remove the global field (or set it to false) for user-scoped branch listing.
  2. If global branch listing is needed, have an admin perform the request.

Example fix

// before — non-admin trying global branches
fetch('/api/extensions/branches', {
  method: 'POST',
  body: JSON.stringify({ extensionName: 'my-ext', global: true }),
});

// after — user-scoped
fetch('/api/extensions/branches', {
  method: 'POST',
  body: JSON.stringify({ extensionName: 'my-ext' }),
});
Defensive patterns

Strategy: validation

Validate before calling

// Only set global when the user is an admin.
function buildBranchesPayload(extensionName, isAdmin, globalRequested) {
  const payload = { extensionName };
  if (globalRequested && isAdmin) {
    payload.global = true;
  } else if (globalRequested && !isAdmin) {
    console.warn('User is not admin — falling back to user-scoped branch listing.');
  }
  return payload;
}

const payload = buildBranchesPayload(name, user.profile.admin, wantGlobal);
await fetch('/api/extensions/branches', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload),
});

Type guard

/** Checks whether the current user may list branches of global extensions. */
function canListGlobalBranches(user) {
  return Boolean(user?.profile?.admin === true);
}

Prevention

When it happens

Trigger: POST /api/extensions/branches with { extensionName: "...", global: true } (or any truthy value) when request.user.profile.admin is falsy.

Common situations: Non-admin user has global: true set inadvertently; frontend bug sends the flag; or the user misunderstands their privilege level.

Understand the failure class

Related errors


AI-assisted analysis of SillyTavern/SillyTavern@8172dcd0ee (2026-08-13). Data as JSON: /api/errors/87a6b08152a19d18. Report an issue: GitHub.