grafana/k6 · error

%q is an invalid permission

Error message

%q is an invalid permission

What it means

browserContext.grantPermissions(permissions) validates each string against a fixed allowlist mapped to CDP permission types (browser_context.go:243): geolocation, midi, midi-sysex, notifications, camera, microphone, background-sync, ambient-light-sensor, accelerometer, gyroscope, magnetometer, clipboard-read, clipboard-write, payment-handler. Any other string fails fast with '%q is an invalid permission' before any CDP call is made — a pure input-validation error.

Source

Thrown at internal/js/modules/k6/browser/common/browser_context.go:243

		"midi-sysex":           cdpbrowser.PermissionTypeMidiSysex,
		"notifications":        cdpbrowser.PermissionTypeNotifications,
		"camera":               cdpbrowser.PermissionTypeVideoCapture,
		"microphone":           cdpbrowser.PermissionTypeAudioCapture,
		"background-sync":      cdpbrowser.PermissionTypeBackgroundSync,
		"ambient-light-sensor": cdpbrowser.PermissionTypeSensors,
		"accelerometer":        cdpbrowser.PermissionTypeSensors,
		"gyroscope":            cdpbrowser.PermissionTypeSensors,
		"magnetometer":         cdpbrowser.PermissionTypeSensors,
		"clipboard-read":       cdpbrowser.PermissionTypeClipboardReadWrite,
		"clipboard-write":      cdpbrowser.PermissionTypeClipboardSanitizedWrite,
		"payment-handler":      cdpbrowser.PermissionTypePaymentHandler,
	}

	perms := make([]cdpbrowser.PermissionType, 0, len(permissions))
	for _, p := range permissions {
		proto, ok := permsToProtocol[p]
		if !ok {
			return fmt.Errorf("%q is an invalid permission", p)
		}
		perms = append(perms, proto)
	}

	action := cdpbrowser.GrantPermissions(perms).WithOrigin(opts.Origin).WithBrowserContextID(b.id)
	if err := action.Do(cdp.WithExecutor(b.ctx, b.browser.conn)); err != nil {
		return fmt.Errorf("granting browser permissions: %w", err)
	}

	return nil
}

// NewPage creates a new page inside this browser context.
func (b *BrowserContext) NewPage() (*Page, error) {
	b.logger.Debugf("BrowserContext:NewPage", "bctxid:%v", b.id)
	_, span := TraceAPICall(b.ctx, "", "browserContext.newPage")
	defer span.End()

View on GitHub (pinned to 93accf6570)

Solutions

  1. Use only the supported names: geolocation, midi, midi-sysex, notifications, camera, microphone, background-sync, ambient-light-sensor, accelerometer, gyroscope, magnetometer, clipboard-read, clipboard-write, payment-handler
  2. Check spelling, case and whitespace in the permissions array
  3. Drop permissions k6 does not support rather than mapping them to nearest-match names

Example fix

// before
ctx.grantPermissions(['persistent-storage', 'geolocation']) // throws on persistent-storage

// after
ctx.grantPermissions(['geolocation'])
Defensive patterns

Strategy: type-guard

Validate before calling

const K6_PERMISSIONS = new Set([
  'geolocation', 'midi', 'midi-sysex', 'notifications', 'camera', 'microphone',
  'background-sync', 'ambient-light-sensor', 'accelerometer', 'gyroscope',
  'magnetometer', 'clipboard-read', 'clipboard-write', 'payment-handler',
])
const perms = perms.filter((p) => K6_PERMISSIONS.has(p))
if (perms.length) ctx.grantPermissions(perms)

Type guard

function isValidK6Permission(p) {
  return ['geolocation','midi','midi-sysex','notifications','camera','microphone',
    'background-sync','ambient-light-sensor','accelerometer','gyroscope',
    'magnetometer','clipboard-read','clipboard-write','payment-handler'].includes(p)
}

Prevention

When it happens

Trigger: Passing a Playwright-only name such as 'persistent-storage', 'push', 'clipboard-sanitized-write' or 'accessibility-events'; typos like 'geolocations' or 'camera '.

Common situations: Porting Playwright permission lists that are longer than k6's allowlist; copy-paste from browser docs of a different tool; case or whitespace differences.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/0422a3608bd3d606. Report an issue: GitHub.