Joplin proxy not working

I use Joplin on Windows and Linux. Netshare runs in Android acting as hot spot using proxy address: my Joplin can never sync through it.
I am using Joplin version 2.9.17 (prod, linux/windows)
my sync server is on one drive

  1. I enable proxy in Joplin
  2. put for proxy url
    when sync it gives:
    Last error: TypeError [ERR_INVALID_URL]: Could not initialise synchroniser: Invalid URL
    Fetching resources: 1/1

My pc works with Netshare without issues

Could you please check the console output for the error Could not initialise synchroniser: Invalid URL? The console shows more useful information

To check the output:

  1. Open Joplin, Click Help->Toggle development tools

  2. Synchronize

  3. The debug console should output the Access denied error with details. Copy them and post them there.

I am not sure where can I find the console output ? I don't use command line to start Joplin BTW.

I just edited my last post. You can check it.



Just Click Console near the top-left corner of the development tools then you will see the errors

const moment = require('moment');
import time from './time';
const { FsDriverDummy } = require('./fs-driver-dummy.js');
const { sprintf } = require('sprintf-js');
const Mutex = require('async-mutex').Mutex;

const writeToFileMutex_ = new Mutex();

export enum TargetType {
Database = 'database',
File = 'file',
Console = 'console',

export enum LogLevel {
None = 0,
Error = 10,
Warn = 20,
Info = 30,
Debug = 40,

interface TargetOptions {
level?: LogLevel;
database?: any;
console?: any;
prefix?: string;
path?: string;
source?: string;

// Default message format
format?: string;

// If specified, will use this as format if it's an info message
formatInfo?: string;


interface Target extends TargetOptions {
type: TargetType;

export interface LoggerWrapper {
debug: Function;
info: Function;
warn: Function;
error: Function;

class Logger {

// For backward compatibility
public static LEVEL_NONE = LogLevel.None;
public static LEVEL_ERROR = LogLevel.Error;
public static LEVEL_WARN = LogLevel.Warn;
public static LEVEL_INFO = LogLevel.Info;
public static LEVEL_DEBUG = LogLevel.Debug;

public static fsDriver_: any = null;
private static globalLogger_: Logger = null;

private targets_: Target[] = [];
private level_: LogLevel = LogLevel.Info;
private lastDbCleanup_: number = time.unixMs();
private enabled_: boolean = true;

static fsDriver() {
	if (!Logger.fsDriver_) Logger.fsDriver_ = new FsDriverDummy();
	return Logger.fsDriver_;

public get enabled(): boolean {
	return this.enabled_;

public set enabled(v: boolean) {
	this.enabled_ = v;

public static initializeGlobalLogger(logger: Logger) {
	this.globalLogger_ = logger;

public static get globalLogger(): Logger {
	if (!this.globalLogger_) throw new Error('Global logger has not been initialized!!');
	return this.globalLogger_;

static create(prefix: string): LoggerWrapper {
	return {
		debug: (...object: any[]) => this.globalLogger.log(LogLevel.Debug, prefix, ...object),
		info: (...object: any[]) => this.globalLogger.log(LogLevel.Info, prefix, ...object),
		warn: (...object: any[]) => this.globalLogger.log(LogLevel.Warn, prefix, ...object),
		error: (...object: any[]) => this.globalLogger.log(LogLevel.Error, prefix, ...object),

public setLevel(level: LogLevel) {
	const previous = this.level_;
	this.level_ = level;
	return previous;

level() {
	return this.level_;

targets() {
	return this.targets_;

addTarget(type: TargetType, options: TargetOptions = null) {
	const target = { type: type };
	for (const n in options) {
		if (!options.hasOwnProperty(n)) continue;
		(target as any)[n] = (options as any)[n];


objectToString(object: any) {
	let output = '';

	if (typeof object === 'object') {
		if (object instanceof Error) {
			object = object as any;
			output = object.toString();
			if (object.code) output += `\nCode: ${object.code}`;
			if (object.headers) output += `\nHeader: ${JSON.stringify(object.headers)}`;
			if (object.request) output += `\nRequest: ${object.request.substr ? object.request.substr(0, 1024) : ''}`;
			if (object.stack) output += `\n${object.stack}`;
		} else {
			output = JSON.stringify(object);
	} else {
		output = object;

	return output;

objectsToString(...object: any[]) {
	const output = [];
	for (let i = 0; i < object.length; i++) {
	return output.join(', ');

static databaseCreateTableSql() {
	const output = `
		source TEXT,
		level INT NOT NULL,
		message TEXT NOT NULL,
		\`timestamp\` INT NOT NULL
	return output.split('\n').join(' ');

// Only for database at the moment
async lastEntries(limit: number = 100, options: any = null) {
	if (options === null) options = {};
	if (!options.levels) options.levels = [LogLevel.Debug, LogLevel.Info, LogLevel.Warn, LogLevel.Error];
	if (!options.levels.length) return [];

	for (let i = 0; i < this.targets_.length; i++) {
		const target = this.targets_[i];
		if (target.type === 'database') {
			let sql = `SELECT * FROM logs WHERE level IN (${options.levels.join(',')}) ORDER BY timestamp DESC`;
			if (limit !== null) sql += ` LIMIT ${limit}`;
			return await target.database.selectAll(sql);
	return [];

targetLevel(target: Target) {
	if ('level' in target) return target.level;
	return this.level();

public log(level: LogLevel, prefix: string, ...object: any[]) {
	if (!this.targets_.length || !this.enabled) return;

	for (let i = 0; i < this.targets_.length; i++) {
		const target = this.targets_[i];
		const targetPrefix = prefix ? prefix : target.prefix;

		if (this.targetLevel(target) < level) continue;

		if (target.type === 'console') {
			let fn = 'log';
			if (level === LogLevel.Error) fn = 'error';
			if (level === LogLevel.Warn) fn = 'warn';
			if (level === LogLevel.Info) fn = 'info';
			const consoleObj = target.console ? target.console : console;
			let items: any[] = [];

			if (target.format) {
				const format = level === LogLevel.Info && target.formatInfo ? target.formatInfo : target.format;

				const s = sprintf(format, {
					date_time: moment().format('YYYY-MM-DD HH:mm:ss'),
					level: Logger.levelIdToString(level),
					prefix: targetPrefix || '',
					message: '',

				items = [s.trim()].concat(...object);
			} else {
				const prefixItems = [moment().format('HH:mm:ss')];
				if (targetPrefix) prefixItems.push(targetPrefix);
				items = [`${prefixItems.join(': ')}:`].concat(...object);

		} else if (target.type === 'file') {
			const timestamp = moment().format('YYYY-MM-DD HH:mm:ss');
			const line = [timestamp];
			if (targetPrefix) line.push(targetPrefix);

			// Write to file using a mutex so that log entries appear in the
			// correct order (otherwise, since the async call is not awaited
			// by caller, multiple log call in a row are not guaranteed to
			// appear in the right order). We also can't use a sync call
			// because that would slow down the main process, especially
			// when many log operations are being done (eg. during sync in
			// dev mode).
			let release: Function = null;
			// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
			writeToFileMutex_.acquire().then((r: Function) => {
				release = r;
				return Logger.fsDriver().appendFile(target.path, `${line.join(': ')}\n`, 'utf8');
				// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
			}).catch((error: any) => {
				console.error('Cannot write to log file:', error);
				// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
			}).finally(() => {
				if (release) release();
		} else if (target.type === 'database') {
			const msg = [];
			if (targetPrefix) msg.push(targetPrefix);

			const queries = [
					sql: 'INSERT INTO logs (`source`, `level`, `message`, `timestamp`) VALUES (?, ?, ?, ?)',
					params: [target.source, level, msg.join(': '), time.unixMs()],

			const now = time.unixMs();
			if (now - this.lastDbCleanup_ > 1000 * 60 * 60) {
				this.lastDbCleanup_ = now;
				const dayKeep = 14;
					sql: 'DELETE FROM logs WHERE `timestamp` < ?',
					params: [now - 1000 * 60 * 60 * 24 * dayKeep],


error(...object: any[]) {
	return this.log(LogLevel.Error, null, ...object);
warn(...object: any[]) {
	return this.log(LogLevel.Warn, null, ...object);
info(...object: any[]) {
	return this.log(LogLevel.Info, null, ...object);
debug(...object: any[]) {
	return this.log(LogLevel.Debug, null, ...object);

static levelStringToId(s: string) {
	if (s === 'none') return LogLevel.None;
	if (s === 'error') return LogLevel.Error;
	if (s === 'warn') return LogLevel.Warn;
	if (s === 'info') return LogLevel.Info;
	if (s === 'debug') return LogLevel.Debug;
	throw new Error(`Unknown log level: ${s}`);

static levelIdToString(id: LogLevel) {
	if (id === LogLevel.None) return 'none';
	if (id === LogLevel.Error) return 'error';
	if (id === LogLevel.Warn) return 'warn';
	if (id === LogLevel.Info) return 'info';
	if (id === LogLevel.Debug) return 'debug';
	throw new Error(`Unknown level ID: ${id}`);

static levelIds() {
	return [LogLevel.None, LogLevel.Error, LogLevel.Warn, LogLevel.Info, LogLevel.Debug];


export default Logger;

a lot of text in there, I just click on the last(bottom) one "logger.ts:219" it bring me the source tag. Below is console message:

09:55:23: TypeError: Invalid URL
at __node_internal_captureLargerStackTrace (node:internal/errors:464:5)
at new NodeError (node:internal/errors:371:5)
at onParseError (node:internal/url:552:9)
at new URL (node:internal/url:628:5)
at new HttpsProxyAgent (/tmp/.mount_JoplinMr…agent/index.js:66:9)
at shim.proxyAgent (/tmp/.mount_JoplinMr…init-node.js:623:11)
at shim.fetch (/tmp/.mount_JoplinMr…init-node.js:454:75)
at OneDriveApi. (onedrive-api.ts:285:28)
at ()
at /tmp/.mount_JoplinMr…nedrive-api.js:8:71
at new Promise ()
at __awaiter (/tmp/.mount_JoplinMr…nedrive-api.js:4:12)
at OneDriveApi.exec (onedrive-api.ts:230:99)
at OneDriveApi. (onedrive-api.ts:387:31)
at ()
at /tmp/.mount_JoplinMr…nedrive-api.js:8:71
at new Promise ()
at __awaiter (/tmp/.mount_JoplinMr…nedrive-api.js:4:12)
at OneDriveApi.execJson (onedrive-api.ts:386:82)
at OneDriveApi. (onedrive-api.ts:82:24)
at ()
at /tmp/.mount_JoplinMr…nedrive-api.js:8:71
at new Promise ()
at __awaiter (/tmp/.mount_JoplinMr…nedrive-api.js:4:12)
at OneDriveApi.appDirectory (onedrive-api.ts:80:20)
at SyncTargetOneDrive. (SyncTargetOneDrive.ts:105:35)
at ()
at /tmp/.mount_JoplinMr…getOneDrive.js:8:71
at new Promise ()
at __awaiter (/tmp/.mount_JoplinMr…getOneDrive.js:4:12)
at SyncTargetOneDrive.initFileApi (SyncTargetOneDrive.ts:95:19)
at SyncTargetOneDrive. (BaseSyncTarget.ts:93:30)
at ()
at /tmp/.mount_JoplinMr…eSyncTarget.js:8:71
at new Promise ()
at __awaiter (/tmp/.mount_JoplinMr…eSyncTarget.js:4:12)
at SyncTargetOneDrive.fileApi (BaseSyncTarget.ts:91:22)
at SyncTargetOneDrive. (SyncTargetOneDrive.ts:115:50)
at ()
at fulfilled (/tmp/.mount_JoplinMr…getOneDrive.js:5:58)

Oh I think you should set the Proxy URL as, rather than

I did it and just did it again. not working

I also did
not working

Well I'm sure the Proxy URL without scheme is wrong

Test result

without scheme:

with scheme:

I just noticed that your Proxy URL is instead of Are you sure your URL is correct? I think is not common for a LAN IP address.
Oh sorry that's my typo. You should try
I'm so sorry for that.

I am sorry, I changed it to
I got this

At least it shows the proxy is working now
How about

I got

I just searched the GitHub issues and topics in the forum. Then I found that the error EPROTO...WRONG_VERSION_NUMBER is something about OpenSSL, and I didn't find a generic solution for it. It can be caused by the OS, proxy, Joplin or OneDrive.
Considering that this error is not common, I guess it's something wrong in your proxy?

I am not a network expert. Several points I would mention are:

  1. this proxy works for my PC(linux and windows) when set proxy to it required
  2. this proxy works for other applications when set their proxy IP: Port: 8282
  3. this proxy works for apt, snap update or installation when set that ip:port in corresponding configure files.

Right now the only thing I can't make it working is Joplin

But I don't see anyone using v2.9.17 with OneDrive reporting this problem. It's rare.