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
- 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.
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
- 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.
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
- usage_missing_arg
- export response missing data field
- reconnect stream body missing
- usage_missing_arg
- not_logged_in
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/e18d5fe167d31d5c.
Report an issue: GitHub.