Mintplex-Labs/anything-llm · error

${res.error || "Failed to save flow"}

Error message

${res.error || "Failed to save flow"}

What it means

Thrown from `saveFlow` on a non-ok `POST /api/agent-flows/save`. NOTE: `res.error` is read off the fetch `Response` object, which has NO `.error` property — `res.error` is always `undefined`, so this expression ALWAYS falls through to the literal 'Failed to save flow', discarding any server-provided error message. This is a defect; compare with communityHub models that `await res.json()` first then read `response.error`.

Source

Thrown at frontend/src/models/agentFlows.js:22

const AgentFlows = {
  /**
   * Save a flow configuration
   * @param {string} name - Display name of the flow
   * @param {object} config - The configuration object for the flow
   * @param {string} [uuid] - Optional UUID for updating existing flow
   * @returns {Promise<{success: boolean, error: string | null, flow: {name: string, config: object, uuid: string} | null}>}
   */
  saveFlow: async (name, config, uuid = null) => {
    return await fetch(`${API_BASE}/agent-flows/save`, {
      method: "POST",
      headers: {
        ...baseHeaders(),
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ name, config, uuid }),
    })
      .then((res) => {
        if (!res.ok) throw new Error(res.error || "Failed to save flow");
        return res;
      })
      .then((res) => res.json())
      .catch((e) => ({
        success: false,
        error: e.message,
        flow: null,
      }));
  },

  /**
   * List all available flows in the system
   * @returns {Promise<{success: boolean, error: string | null, flows: Array<{name: string, uuid: string, description: string, steps: Array}>}>}
   */
  listFlows: async () => {
    return await fetch(`${API_BASE}/agent-flows/list`, {
      method: "GET",
      headers: baseHeaders(),

View on GitHub (pinned to 526360e320)

Solutions

  1. Fix the defect: parse the JSON body first, then read `response.error` (mirroring communityHub.js).
  2. While debugging, check the browser network tab — the response body carries the real error.
  3. On 401 re-authenticate; on 400 fix the payload per the server message.
  4. Add a test asserting the server error message propagates to the thrown Error.

Example fix

// before
.then((res) => {
  if (!res.ok) throw new Error(res.error || 'Failed to save flow'); // res.error is always undefined
  return res;
})

// after — read the JSON body so the server's error survives
.then(async (res) => {
  const response = await res.json();
  if (!res.ok) throw new Error(response?.error || `Failed to save flow (HTTP ${res.status})`);
  return response;
})
Defensive patterns

Strategy: try-catch

Validate before calling

// The bug is in the model. Caller-side, validate inputs and inspect network:
function validateFlowPayload(name, config) {
  if (typeof name !== 'string' || !name.trim()) throw new Error('Flow name is required');
  if (!config || typeof config !== 'object') throw new Error('Flow config is required');
}

Type guard

function isFlowPayloadValid(name, config) {
  return typeof name === 'string' && name.trim().length > 0 && !!config && typeof config === 'object';
}

Try / catch

const { success, error, flow } = await AgentFlows.saveFlow(name, config, uuid);
if (!success) {
  // NOTE: error is the generic 'Failed to save flow' until the model bug is fixed.
  // Inspect the network tab for the server's actual message.
  showToast(error);
}

Prevention

When it happens

Trigger: Any non-2xx from the save endpoint: invalid flow config, duplicate name/uuid, backend validation failure, 401/403, or 500. In every case the user sees only 'Failed to save flow', not the real reason.

Common situations: User saves a flow with a name collision or malformed config; session expires; backend schema changes; debugging is hard because the helpful server message is dropped.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@526360e320 (2026-08-13). Data as JSON: /api/errors/eedc612347d5c441. Report an issue: GitHub.