fix(cli): JSON error envelope shows command name + clearer rate-limit message

Two papercuts surfaced when prod's bootstrap returned 403:

  {"status": "error", "command": "", "error": "Bootstrap failed: {\"detail\":\"You do not have permission to perform this action.\"}", "data": null}

1. `"command": ""` — the JSON error envelope (printError under agent
   mode) reads from current_command state, but nothing ever called
   setCurrentCommand on the init subcommand. Hook into Node's
   preAction and Python's main_callback to stash the active
   subcommand name so error envelopes report which command failed.

2. Opaque rate-limit message. The backend's @ratelimit decorator
   raises PermissionDenied → DRF translates to generic 403
   "You do not have permission to perform this action." Users don't
   know it's a rate limit. Detect status_code==403 with /permission/i
   in the detail and surface the actual reason:
   "Daily Agent Mode signup limit reached for this network (5/day).
   Try again from a different IP or after midnight UTC."

Both fixes mirrored in Python (agent_mode_cmd.py, app.py) and Node
(agent-mode.ts, index.ts).
This commit is contained in:
Mgeeeek
2026-05-14 16:43:17 +05:30
parent 844d633960
commit f21e9fe2b3
4 changed files with 37 additions and 5 deletions
+12 -2
View File
@@ -64,11 +64,21 @@ export async function bootstrapViaBackend(
if (!resp.ok) {
let detail: string = resp.statusText;
try {
const errBody = (await resp.json()) as { error?: string };
if (errBody.error) detail = errBody.error;
const body = (await resp.json()) as { error?: string; detail?: string };
detail = body.error ?? body.detail ?? resp.statusText;
} catch {
/* leave detail as statusText */
}
// Backend's @ratelimit decorator raises PermissionDenied, which DRF
// translates to a generic 403 "You do not have permission to perform
// this action." That's opaque — surface it as the rate-limit message
// it actually is.
if (resp.status === 403 && /permission/i.test(detail)) {
printError(
"Daily Agent Mode signup limit reached for this network (5/day). Try again from a different IP or after midnight UTC.",
);
process.exit(1);
}
printError(`Bootstrap failed: ${detail}`);
process.exit(1);
}
+5 -1
View File
@@ -13,7 +13,7 @@ import { colors, printError, printWarning } from "./branding.js";
import type { Mem0Config } from "./config.js";
import { loadConfig, saveConfig } from "./config.js";
import { richFormatHelp } from "./help.js";
import { isAgentMode, setAgentMode, takeNotice } from "./state.js";
import { isAgentMode, setAgentMode, setCurrentCommand, takeNotice } from "./state.js";
import { captureEvent } from "./telemetry.js";
import { CLI_VERSION } from "./version.js";
@@ -171,6 +171,10 @@ program.hook("preAction", (_thisCommand, actionCommand) => {
parentName && parentName !== "mem0"
? `${parentName}.${commandName}`
: commandName;
// Stash the active command name in shared state so the JSON
// error envelope (printError) can report which command failed
// instead of an empty `"command": ""` field.
setCurrentCommand(fullCommand);
// init fires its own telemetry from runInit with full M1-M6 props
// (mode/agent_caller/signup_source/claimed_agent_mode); skip the
// auto-fire here so we don't double-count.