langgenius/dify · error · BaseError

not_logged_in

not_logged_in

Error message

account has no email; cannot store credential

What it means

Raised by CustomizedPipelineTemplateApi.post (POST /rag/pipeline/customized/templates/{template_id}) when the SELECT on PipelineCustomizedTemplate by id returns no row. This endpoint returns the stored yaml_content for a customized template. Note this raises a bare ValueError — not werkzeug NotFound — so Flask surfaces it as HTTP 500 rather than 404; this is arguably a bug in status-code mapping. The session is opened in a with block and the check runs inside it.

Source

Thrown at cli/src/commands/auth/login/login.ts:197

      w.write(
        `${cs.successIcon()} Logged in to ${display} as ${cs.bold(s.subject_email)} (external SSO)\n`,
      )
    return
  }
  w.write(`${cs.successIcon()} Logged in to ${display}\n`)
}

function findDefaultWorkspace(
  s: PollSuccess,
): { id: string; name: string; role: string } | undefined {
  if (s.default_workspace_id === undefined || s.default_workspace_id === '') return undefined
  return s.workspaces?.find((w) => w.id === s.default_workspace_id)
}

function accountEmail(s: PollSuccess): string {
  const email = (s.account?.email ?? '') !== '' ? s.account!.email : (s.subject_email ?? '')
  if (email === '') {
    throw new BaseError({
      code: ErrorCode.NotLoggedIn,
      message: 'account has no email; cannot store credential',
      hint: 'this Dify instance returned no email for the signed-in subject',
    })
  }
  return email
}

function contextFromSuccess(s: PollSuccess): AccountContext {
  const ctx: AccountContext = {
    account: s.account
      ? { id: s.account.id, email: s.account.email, name: s.account.name }
      : { id: '', email: '', name: '' },
    token_id: s.token_id,
  }
  if (
    s.subject_email !== undefined &&
    s.subject_email !== '' &&

View on GitHub (pinned to ef8544b173)

Solutions

  1. Refresh the customized template list and use a current template_id.
  2. If the template was deleted, recreate it or choose another.
  3. Handle the 500 (ValueError) response defensively in the client and reload the list — and file/fix the controller to raise NotFound for a proper 404.

Example fix

# before
if not template:
    raise ValueError("Customized pipeline template not found.")
# after — return a proper 404
from werkzeug.exceptions import NotFound
if not template:
    raise NotFound("Customized pipeline template not found.")
Defensive patterns

Strategy: validation

Validate before calling

async function customizedTemplateExists(client, templateId: string): Promise<boolean> {
  const r = await client.get(`/console/api/rag/pipeline/templates?type=customized`);
  const list = await r.json();
  return list.pipeline_templates?.some(t => t.id === templateId) ?? false;
}

Type guard

function isKnownCustomizedTemplate(id: string, known: Set<string>): boolean { return known.has(id); }

Try / catch

try {
  return await client.post(`/rag/pipeline/customized/templates/${templateId}`);
} catch (e) {
  // NOTE: controller raises ValueError -> HTTP 500; treat 500-with-this-message as not-found
  if (e.response?.status === 500 && /Customized pipeline template not found/i.test(e.response.data?.message || '')) {
    await refreshCustomizedTemplateList();
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /console/api/rag/pipeline/customized/templates/{template_id} where template_id was never created, was deleted via the DELETE sibling endpoint, or was created by a different tenant (the query has no tenant filter at this line, but the row genuinely must exist).

Common situations: Template was deleted between the UI listing it and the user clicking to load its yaml; a stale template_id cached in the UI; cross-instance inconsistency where the row exists on the writer but not yet on the reader.

Related errors


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