Fix Common CloudBase Authentication Errors in WeChat Mini Programs
In one sentence: Match your error code to the right section and quickly resolve the 10 most common errors when integrating CloudBase Custom Login in a WeChat Mini Program.
Estimated time: 10–20 minutes (depending on which error you hit) | Difficulty: Beginner
Applicable Scenarios
This recipe is for projects that have already completed the basic flow from add-auth-wechat-miniprogram but are hitting a specific error at some step. Each section follows the pattern: Symptom → Root Cause → Fix (with code) → Prevention. The goal is to understand why the error occurs, not just how to work around it.
- Applicable: Mini Program projects that have integrated CloudBase Custom Login and are hitting errors
- Not applicable: Projects that haven't completed the basic integration yet — set up the environment with the add recipe first, then come back here
Usage: DevTools Console → copy the error keyword → Ctrl+F in the table of contents below → jump to the relevant section.
Prerequisites
Same environment as the add recipe:
@cloudbase/js-sdk@2.27.3+@cloudbase/adapter-wx_mp@1.3.1(Mini Program side)@cloudbase/node-sdk@3.18.1(Cloud Function)- WeChat DevTools ≥
1.06.x
Error 1 — require is not defined (Mini Program side)
Symptom: After compiling the Mini Program, Console immediately shows ReferenceError: require is not defined, usually pointing to libs/cloudbase.js or miniprogram_npm/@cloudbase/js-sdk/index.js.
Root Cause: "Build npm" was not executed. The WeChat Mini Program runtime does not directly recognize node_modules — you must use DevTools to build dependencies into the miniprogram_npm/ directory first.
Fix:
- WeChat DevTools → Tools → Build npm
- Verify that
miniprogram_npm/@cloudbase/andminiprogram_npm/@cloudbase/adapter-wx_mp/appear in the project root - Recompile
Prevention: Add "Build npm" to the team's local startup checklist. It must be re-run every time you install new dependencies or switch to a branch with package.json changes — it does not trigger automatically.
Error 2 — WeChat returns errcode: 40029
Symptom: Cloud Function logs contain:
{ "errcode": 40029, "errmsg": "invalid code, hints: ..." }
Root Cause: The code returned by wx.login has already been used once, or has expired (valid for 5 minutes). WeChat's rule is that a code can only be exchanged for a session once — a second call returns 40029.
Fix: Call wx.login fresh every login flow — do not cache the code in storage or reuse it across pages:
// ❌ Don't do this
const cachedCode = wx.getStorageSync('wx_login_code');
// ✅ Get a fresh code each time
const { code } = await new Promise((resolve, reject) => {
wx.login({ success: resolve, fail: reject });
});
Prevention: The ensureLogin function should have no shortcuts that bypass wx.login. All "already logged in?" checks should go through auth.hasLoginState() — never maintain a code cache.
Error 3 — WeChat returns errcode: 45011
Symptom:
{ "errcode": 45011, "errmsg": "api minute-quota reach limit" }
Root Cause: The jscode2session API has a per-minute rate limit per IP. This error is most common during gradual rollouts and almost never seen during single-user debugging.
Fix: Add a ticket cache layer in the Cloud Function, keyed by openid, TTL 5–10 minutes (a ticket's own validity is 10 minutes, so caching longer is pointless). The simplest approach is to use the cloud database as a cache:
const db = app.database();
const TICKET_CACHE = 'login_ticket_cache';
async function getCachedTicket(openid) {
const res = await db
.collection(TICKET_CACHE)
.where({ openid })
.get();
if (res.data.length === 0) return null;
const record = res.data[0];
if (Date.now() - record.createdAt > 5 * 60 * 1000) return null;
return record.ticket;
}
async function saveTicketCache(openid, ticket) {
await db.collection(TICKET_CACHE).add({
data: { openid, ticket, createdAt: Date.now() },
});
}
For higher traffic, switch to Redis (available as a CloudBase extension).
Prevention: During load testing, actively observe the threshold at which 45011 appears and add caching before hitting it — don't wait until production.
Error 4 — INVALID_TICKET / TICKET_EXPIRED
Symptom: Mini Program side calling signInWithCustomTicket returns:
{ code: 'INVALID_TICKET', message: 'ticket is invalid or expired' }
Root Cause: Three possibilities in order of likelihood:
tcb_custom_login.jsonis not the latest copy from the Console — each time you click "Regenerate Private Key" in the Console, the old key expires 2 hours later. This is the most common cause, typically in multi-developer scenarios where one person re-downloaded the key and another still has the old one locally.- The Cloud Function was deployed with an expired private key, so tickets it signs are already invalid.
- The ticket was truncated in transit between the Cloud Function and the Mini Program (e.g., double-serialized with
JSON.stringify).
Fix:
# 1. Re-download the latest tcb_custom_login.json from the Console
# 2. Overwrite the old file in the Cloud Function directory
cp ~/Downloads/tcb_custom_login.json cloudfunctions/getLoginTicket/
# 3. Redeploy
tcb fn deploy getLoginTicket --dir ./cloudfunctions/getLoginTicket -e your-env-id
Prevention: Log ticket length in the Cloud Function as a sanity check:
const ticket = app.auth().createTicket(openid);
console.log('[getLoginTicket] ticket length:', ticket.length);
A valid ticket length should be consistently in the hundreds of characters. If the length is wrong, the ticket was malformed at signing time.
Error 5 — SYSTEM_NOT_OPEN (Custom Login not enabled)
Symptom:
{ "code": "SYSTEM_NOT_OPEN", "message": "custom login is not enabled" }
Root Cause: Custom Login is not enabled in the CloudBase Console under Authentication → Login Methods. You can download the private key without enabling the feature — createTicket won't fail, but the signed ticket will be rejected at login time.
Fix: Console → Authentication → Login Methods → Custom Login → Enable.
Prevention: Include the enabled status as a pre-deployment check, or write a startup self-check Cloud Function to run in CI.
Error 6 — customUserId format error
Symptom:
createTicket failed: customUserId must match /^[A-Za-z0-9_\-#@(){}\[\]:.,<>+#~]{4,32}$/
Root Cause: The first argument passed to createTicket is not a valid customUserId. The rule is 4–32 characters, letters/digits and select symbols only.
Fix: Validate the jscode2session response before passing it:
const session = await jscode2session(code);
if (session.errcode) {
// WeChat-side error — return early
return { error: 'WX_API_ERROR', errcode: session.errcode };
}
if (!session.openid) {
// Should not happen in practice, but defensive guard
return { error: 'NO_OPENID', raw: session };
}
const ticket = app.auth().createTicket(session.openid);
A real openid is 28 alphanumeric characters and satisfies the format rule. This error almost always means the WeChat API returned an error, the openid field is absent, and the code passed undefined to createTicket without a guard.
Prevention: Always validate key fields from external API responses before passing them downstream. Never assume a call succeeded.
Error 7 — Mini Program reports NETWORK_ERROR / request:fail url not in domain list
Symptom: When calling the Cloud Function HTTP endpoint:
request:fail url not in domain list
or a similar domain allowlist error.
Root Cause: WeChat Mini Programs require all wx.request target domains to be in the "Server Domain" allowlist configured in the Mini Program Admin Console. CloudBase Cloud Function HTTP Access Service URLs are not there by default.
Fix: Two steps:
- Go to WeChat Official Accounts Platform → your Mini Program → Development → Development Management → Development Settings → Server Domain
- Add your CloudBase HTTP Access Service domain to the
requestallowlist, e.g.https://your-env.service.tcloudbase.com
Note: enter the domain only — do not include the path.
Prevention: During development you can check "Do not verify domain names" in DevTools → Details → Local Settings to bypass this quickly. But before going live, you must configure it properly in the Official Accounts Platform — otherwise experience versions and production will both fail.
Error 8 — Signature error / SIGN_PARAM_INVALID
Symptom: Cloud Function calling app.auth().createTicket() returns:
SIGN_PARAM_INVALID: credential signature verification failed
Root Cause: The JSON loaded from credentials in cloudbase.init has the wrong format, or the private key was exported from a different Environment (e.g., you downloaded the key from Environment A but set env to Environment B's ID).
Fix:
// Debug: confirm the loaded credentials match the current env
const cred = require('./tcb_custom_login.json');
console.log('[getLoginTicket] cred env:', cred.env);
console.log('[getLoginTicket] init env:', process.env.TCB_ENV);
// These two must be equal
If they don't match, re-download the private key from the target Environment's Console.
Prevention: Don't use tcb_custom_login.json as a single filename across environments. In CI/CD, rename with an env prefix, e.g. tcb_custom_login.prod.json, tcb_custom_login.staging.json, to prevent environment mixing at the source.
Error 9 — auth is not a function (Mini Program side)
Symptom:
TypeError: cloudbase.useAdapters is not a function
or:
TypeError: app.auth is not a function
Root Cause: Wrong version of @cloudbase/js-sdk. Two common cases:
@cloudbase/js-sdk@1.xis installed — this is the old version with a different API from 2.x@cloudbase/js-sdk@next(v3) is installed — the built-in adapter removed theuseAdaptersmethod
Fix: Pin to 2.27.3:
cd miniprogram
npm install --save @cloudbase/js-sdk@2.27.3 @cloudbase/adapter-wx_mp@1.3.1
Then lock the versions in package.json (no ^):
{
"dependencies": {
"@cloudbase/js-sdk": "2.27.3",
"@cloudbase/adapter-wx_mp": "1.3.1"
}
}
Run "Build npm" again.
Prevention: Mini Program frontend dependencies must be pinned to the exact patch version. Floating versions are the most common root cause of the chain-reaction errors above.
Error 10 — Login succeeds but subsequent database / Cloud Function calls return UNAUTHORIZED
Symptom: signInWithCustomTicket appears to succeed, auth.currentUser.uid is available, but a subsequent db.collection('xxx').get() returns UNAUTHORIZED.
Root Cause: A successful login only means "we know who you are." Accessing specific resources is governed by a separate permissions layer. New CloudBase users default to the "External User" role. If the database collection's permissions are set to "Admin Only" or "Organization Members Only," external users cannot read it.
Fix: Choose one of two approaches.
Option A — Relax collection permissions (only for public data):
Console → Database → Collection → Permission Settings → All users can read
Option B — Assign a role to these users (recommended for production):
Console → Authentication → Access Control → Roles → configure specific resource policies for "External Users" or a custom role.
Prevention: During development, set database permissions to a permissive mode (all users read/write) to unblock yourself, then tighten gradually. Debugging from strict to permissive wastes a lot of time.
If Your Error Is Not Listed Above
Self-check in this order:
- Cloud Function logs — CloudBase Console → Cloud Functions → getLoginTicket → Logs. Confirm: does the event carry
code? What didjscode2sessionreturn? WascreateTicketcalled? What did it return? - Mini Program Console — Confirm the
statusCodeanddataof thewx.requestcall. Many issues are at the HTTP layer, not the authentication layer. - CloudBase Console → Authentication → User Management — Check whether the new user was actually persisted. If a record appears, the ticket step worked.
The missing link in the log chain is where the problem is.