{"record":{"id":"e18d5fe167d31d5c","repo":"langgenius/dify","slug":"unknown","errorCode":"unknown","errorMessage":"decode poll response: ${(err as Error).message}","messagePattern":"decode poll response: (.+?)","errorType":"error_code","errorClass":"BaseError","httpStatus":null,"severity":"error","filePath":"cli/src/api/oauth-device.ts","lineNumber":107,"sourceCode":"  }\n\n  async pollOnce(req: PollRequest): Promise<PollResult> {\n    if (req.device_code === '') {\n      throw new BaseError({\n        code: ErrorCode.UsageMissingArg,\n        message: 'device_code is required',\n      })\n    }\n    const body = { client_id: req.client_id ?? DEFAULT_CLIENT_ID, device_code: req.device_code }\n    const res = await this.http.fetch('oauth/device/token', { method: 'POST', json: body })\n    if (res.status === 404) throw versionSkew()\n    if (res.status >= 500) return { status: 'retry_5xx' }\n    let payload: { error?: string } & Partial<PollSuccess> = {}\n    try {\n      const text = await res.text()\n      payload = text === '' ? {} : (JSON.parse(text) as typeof payload)\n    } catch (err) {\n      throw new BaseError({\n        code: ErrorCode.Unknown,\n        message: `decode poll response: ${(err as Error).message}`,\n      })\n    }\n    if (typeof payload.error === 'string' && payload.error !== '') {\n      const status = POLL_ERROR_TO_STATUS[payload.error]\n      if (status === undefined) {\n        throw new BaseError({\n          code: ErrorCode.Unknown,\n          message: `unknown poll error \"${payload.error}\"`,\n        })\n      }\n      return { status } as PollResult\n    }\n    if (typeof payload.token !== 'string' || payload.token === '') {\n      throw new BaseError({\n        code: ErrorCode.Unknown,\n        message: `poll: ${res.status} with no OAuth envelope`,","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/cli/src/api/oauth-device.ts#L89-L125","documentation":"Raised by DatasetMetadataCreateApi.get (GET /datasets/{dataset_id}/metadata). Unlike the POST sibling, this uses DatasetService.get_dataset_for_tenant(dataset_id_str, current_tenant_id), which scopes the lookup by tenant. A dataset that exists but belongs to a different tenant also yields None and therefore this 404. Maps to HTTP 404.","triggerScenarios":"GET /console/api/datasets/{dataset_id}/metadata where the dataset_id does not exist OR exists but belongs to a different tenant than current_tenant_id. The RBAC permission check only runs if RBAC_ENABLED is false; under RBAC the 404 is the only signal returned for cross-tenant access.","commonSituations":"User switched tenants in the console but the URL still references a dataset_id from the previous tenant; cross-tenant collaboration where the user was not added to the owning tenant; deleted dataset.","solutions":["Confirm the user is on the tenant that owns the dataset (check current_tenant_id against dataset.tenant_id).","List datasets under the current tenant to obtain valid IDs.","If the dataset was deleted, recreate it or remove the stale reference."],"exampleFix":"# before — calling from the wrong tenant\nclient.headers['X-Tenant-Id'] = 'tenant-A'\nclient.get(f'/datasets/{tenant_b_dataset_id}/metadata')\n# after\nclient.headers['X-Tenant-Id'] = 'tenant-B'  # switch to the owning tenant\nclient.get(f'/datasets/{tenant_b_dataset_id}/metadata')","handlingStrategy":"validation","validationCode":"async function datasetInTenant(client, datasetId, tenantId) {\n  const r = await client.get(`/console/api/datasets/${datasetId}`, { headers: { 'X-Tenant-Id': tenantId } });\n  return r.status === 200;\n}","typeGuard":"function isCurrentTenant(datasetTenantId: string, currentTenantId: string): boolean {\n  return datasetTenantId === currentTenantId;\n}","tryCatchPattern":"try {\n  return await client.get(`/datasets/${datasetId}/metadata`);\n} catch (e) {\n  if (e.response?.status === 404) {\n    // could be missing OR cross-tenant; reload list under current tenant\n    await reloadDatasetList();\n    return null;\n  }\n  throw e;\n}","preventionTips":["Always issue dataset requests under the tenant that owns the dataset.","On tenant switch, clear all cached dataset_ids and metadata in the UI.","Remember get_dataset_for_tenant collapses missing and cross-tenant into the same 404."],"tags":["dataset","metadata","not-found","tenant-isolation","rest-api","console"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}