actualbudget/actual · error · Error

Authentication failed: ${result.error}

Error message

Authentication failed: ${result.error}

What it means

During init() with password-based config, 'subscribe-sign-in' is called. If the handler returns an error slug (e.g. 'invalid-password' or 'network-failure'), an Error with the message 'Authentication failed: <slug>' is thrown and tagged with that slug as the error code. The message body is a machine-readable slug, so parse the code rather than the text.

Source

Thrown at packages/loot-core/src/server/main.ts:324

          'token-expired',
        );
      }
      if (user.offline === true) {
        // Clear token since we can't validate
        await runHandler(handlers['subscribe-set-token'], { token: '' });
        throw withErrorCode(
          new Error('Authentication failed: server offline or unreachable'),
          'network-failure',
        );
      }
    } else if ('password' in config && config.password) {
      const result = await runHandler(handlers['subscribe-sign-in'], {
        password: config.password,
      });
      if (result?.error) {
        // `result.error` is already a machine-readable slug (e.g.
        // 'invalid-password', 'network-failure')
        throw withErrorCode(
          new Error(`Authentication failed: ${result.error}`),
          result.error,
        );
      }
    }
  } else {
    // This turns off all server URLs. In this mode we don't want any
    // access to the server, we are doing things locally
    setServer(null);

    app.events.on('load-budget', () => {
      setSyncingMode('offline');
    });
  }

  return lib;
}

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Read the error code / message suffix: it is the machine-readable reason
  2. If 'invalid-password', re-enter or reset the sync server password
  3. Verify the server URL points at the intended sync server
  4. If 'network-failure', fix connectivity to the server first, then retry sign-in

Example fix

// before
await init({ URL: serverUrl, password: 'old-pass' });
// after (handle the slug)
try {
  await init({ URL: serverUrl, password });
} catch (e) {
  if (e.code === 'invalid-password') {
    promptUserForPassword();
  } else {
    throw e;
  }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate inputs before calling init with password auth
if (!serverUrl.startsWith('http')) throw new Error('Server URL must be http(s)');
if (!password || password.length === 0) throw new Error('Password required for server auth');

Type guard

function isAuthFailure(e: unknown): e is Error & { code: string } {
  return (
    e instanceof Error &&
    e.message.startsWith('Authentication failed:') &&
    typeof (e as { code?: string }).code === 'string'
  );
}

Try / catch

try {
  await init({ URL: serverUrl, password });
} catch (e) {
  if (isAuthFailure(e)) {
    switch (e.code) {
      case 'invalid-password': return promptPasswordRetry();
      case 'network-failure': return scheduleRetry();
      default: throw e;
    }
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling init({ URL, password }) where the password is wrong ('invalid-password') or the sign-in handler fails with another slug such as 'network-failure' — the exact reason is in both the error code and message suffix.

Common situations: Typo in the server password or password changed on the server; connecting to a fresh server where the user has not been set up; wrong server URL so sign-in fails at the network level.

Understand the failure class

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/29197227c9496390. Report an issue: GitHub.