{"record":{"id":"cecac2e312c086c7","repo":"denoland/deno","slug":"failed-to-execute-query-on-permissions-descri","errorCode":null,"errorMessage":"Failed to execute 'query' on 'Permissions': descriptor required","messagePattern":"Failed to execute 'query' on 'Permissions': descriptor required","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"cli/rt/desktop.rs","lineNumber":653,"sourceCode":"    set onchange(v) { this.#onchange = typeof v === \"function\" ? v : null; }\n  }\n\n  function laufeyToPermissionsApiState(s) {\n    // Spec maps \"prompt\" through verbatim; \"unsupported\" has no spec\n    // analog so we return \"denied\" — query() shouldn't throw, but we\n    // shouldn't lie and say \"granted\" either.\n    switch (s) {\n      case \"granted\": return \"granted\";\n      case \"denied\": return \"denied\";\n      case \"prompt\": return \"prompt\";\n      default: return \"denied\";\n    }\n  }\n\n  const permissionsImpl = {\n    async query(descriptor) {\n      if (descriptor == null || typeof descriptor !== \"object\") {\n        throw new TypeError(\n          \"Failed to execute 'query' on 'Permissions': descriptor required\",\n        );\n      }\n      const name = String(descriptor.name);\n      if (name === \"notifications\") {\n        // No side effects per spec — never call request_*.\n        const s = await op_desktop_query_notification_permission();\n        // Keep Notification.permission's cache in sync: a permissions.query\n        // result is authoritative and lets the synchronous getter report\n        // a current value without us needing a second roundtrip.\n        if (s !== \"unsupported\") {\n          cachedNotificationPermission = laufeyToNotificationPermission(s);\n        }\n        return new PermissionStatus(name, laufeyToPermissionsApiState(s));\n      }\n      // Unknown / unrouted name. Chrome returns \"denied\" for descriptors\n      // it doesn't recognize; mimic that rather than throwing.\n      return new PermissionStatus(name, \"denied\");","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/cli/rt/desktop.rs#L635-L671","documentation":"The desktop runtime's Permissions.query implementation requires a descriptor object. `descriptor == null` or a non-object (string, number) throws this TypeError before any permission name is read.","triggerScenarios":"navigator.permissions.query() with no argument, .query(null), or .query('notifications') — the name must be wrapped in an object: { name: 'notifications' }.","commonSituations":"Passing the permission name as a bare string instead of a descriptor object; copy-paste from APIs that take string parameters.","solutions":["Call query({ name: 'notifications' }).","If the descriptor comes from data, validate it is an object with a name property before calling."],"exampleFix":"// before\nconst s = await navigator.permissions.query('notifications');\n// after\nconst s = await navigator.permissions.query({ name: 'notifications' });","handlingStrategy":"validation","validationCode":"const descriptor = { name: 'notifications' };\nif (typeof descriptor !== 'object' || descriptor === null) {\n  throw new Error('permissions.query needs { name: ... }');\n}\nawait navigator.permissions.query(descriptor);","typeGuard":"function isPermissionDescriptor(v: unknown): v is { name: string } {\n  return typeof v === 'object' && v !== null &&\n    typeof (v as { name?: unknown }).name === 'string';\n}","tryCatchPattern":null,"preventionTips":["Always pass a descriptor object literal: { name: 'notifications' }.","Type the parameter as { name: PermissionName } so a bare string fails at compile time."],"tags":["permissions","notification","desktop","validation"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}