langgenius/dify · error · BaseError

unknown

unknown

Error message

decode poll response: ${(err as Error).message}

What it means

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.

Source

Thrown at cli/src/api/oauth-device.ts:107

  }

  async pollOnce(req: PollRequest): Promise<PollResult> {
    if (req.device_code === '') {
      throw new BaseError({
        code: ErrorCode.UsageMissingArg,
        message: 'device_code is required',
      })
    }
    const body = { client_id: req.client_id ?? DEFAULT_CLIENT_ID, device_code: req.device_code }
    const res = await this.http.fetch('oauth/device/token', { method: 'POST', json: body })
    if (res.status === 404) throw versionSkew()
    if (res.status >= 500) return { status: 'retry_5xx' }
    let payload: { error?: string } & Partial<PollSuccess> = {}
    try {
      const text = await res.text()
      payload = text === '' ? {} : (JSON.parse(text) as typeof payload)
    } catch (err) {
      throw new BaseError({
        code: ErrorCode.Unknown,
        message: `decode poll response: ${(err as Error).message}`,
      })
    }
    if (typeof payload.error === 'string' && payload.error !== '') {
      const status = POLL_ERROR_TO_STATUS[payload.error]
      if (status === undefined) {
        throw new BaseError({
          code: ErrorCode.Unknown,
          message: `unknown poll error "${payload.error}"`,
        })
      }
      return { status } as PollResult
    }
    if (typeof payload.token !== 'string' || payload.token === '') {
      throw new BaseError({
        code: ErrorCode.Unknown,
        message: `poll: ${res.status} with no OAuth envelope`,

View on GitHub (pinned to ef8544b173)

Solutions

  1. Confirm the user is on the tenant that owns the dataset (check current_tenant_id against dataset.tenant_id).
  2. List datasets under the current tenant to obtain valid IDs.
  3. If the dataset was deleted, recreate it or remove the stale reference.

Example fix

# before — calling from the wrong tenant
client.headers['X-Tenant-Id'] = 'tenant-A'
client.get(f'/datasets/{tenant_b_dataset_id}/metadata')
# after
client.headers['X-Tenant-Id'] = 'tenant-B'  # switch to the owning tenant
client.get(f'/datasets/{tenant_b_dataset_id}/metadata')
Defensive patterns

Strategy: validation

Validate before calling

async function datasetInTenant(client, datasetId, tenantId) {
  const r = await client.get(`/console/api/datasets/${datasetId}`, { headers: { 'X-Tenant-Id': tenantId } });
  return r.status === 200;
}

Type guard

function isCurrentTenant(datasetTenantId: string, currentTenantId: string): boolean {
  return datasetTenantId === currentTenantId;
}

Try / catch

try {
  return await client.get(`/datasets/${datasetId}/metadata`);
} catch (e) {
  if (e.response?.status === 404) {
    // could be missing OR cross-tenant; reload list under current tenant
    await reloadDatasetList();
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/e18d5fe167d31d5c. Report an issue: GitHub.