{"record":{"id":"e2010d00ac2e9f00","repo":"laurent22/joplin","slug":"isreadonly","errorCode":"isReadOnly","errorMessage":"error.message","messagePattern":"error\\.message","errorType":"exception","errorClass":"JoplinError","httpStatus":null,"severity":"error","filePath":"packages/lib/file-api-driver-joplinServer.ts","lineNumber":208,"sourceCode":"\t}\n\n\tprivate isReadyOnlyError(error: { code?: string }) {\n\t\treturn error && error.code === 'isReadOnly';\n\t}\n\n\tpublic async put(path: string, content: string | Buffer | null, options: ExecOptions & { shareId?: string } = null) {\n\t\ttry {\n\t\t\tconst output = await this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, options && options.shareId ? { share_id: options.shareId } : null, content, {\n\t\t\t\t'Content-Type': 'application/octet-stream',\n\t\t\t}, options);\n\t\t\treturn output;\n\t\t} catch (error) {\n\t\t\tif (this.isRejectedBySyncTargetError(error)) {\n\t\t\t\tthrow new JoplinError(error.message, 'rejectedByTarget');\n\t\t\t}\n\n\t\t\tif (this.isReadyOnlyError(error)) {\n\t\t\t\tthrow new JoplinError(error.message, 'isReadOnly');\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async multiPut(items: MultiPutItem[], options: ExecOptions = null) {\n\t\tconst output = await this.api().exec('PUT', 'api/batch_items', null, { items: items }, null, options);\n\n\t\tfor (const [, response] of Object.entries<{ error?: { code?: string | number; httpCode?: number } }>(output.items)) {\n\t\t\tif (response.error && this.isRejectedBySyncTargetError(response.error)) {\n\t\t\t\tresponse.error.code = 'rejectedByTarget';\n\t\t\t} else if (response.error && this.isReadyOnlyError(response.error as { code?: string })) {\n\t\t\t\tresponse.error.code = 'isReadOnly';\n\t\t\t}\n\t\t}\n\n\t\treturn output;","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/lib/file-api-driver-joplinServer.ts#L190-L226","documentation":"Re-thrown by the Joplin Server driver when a PUT to .../content fails because the server reports the target share or folder is read-only (error.code === 'isReadOnly'). Wrapped in a JoplinError with code 'isReadOnly' so callers can distinguish permission denial from other failures.","triggerScenarios":"Uploading content to a shared notebook on Joplin Server where the user has read-only permission, or to a share whose permissions were downgraded after it was added locally.","commonSituations":"Share recipient with view-only rights tries to edit/sync; share permission changed server-side; a note was moved into a read-only shared folder; user logged into a different account that lacks write access.","solutions":["Verify the user's permission on the target share/notebook on Joplin Server and request write access if needed.","Move the item out of the read-only share into a writable notebook before editing.","Re-authenticate / re-link the Joplin Server account if permissions changed.","Surface a user-facing message explaining the notebook is read-only rather than retrying blindly."],"exampleFix":"// before\nawait fileApi.put(path, content, { shareId });\n// after\ntry { await fileApi.put(path, content, { shareId }); }\ncatch (e) {\n  if (e.code === 'isReadOnly') {\n    throw new Error('This notebook is read-only; move the note to a writable notebook first.');\n  }\n  throw e;\n}","handlingStrategy":"validation","validationCode":"// If you track share permissions client-side, check before PUT.\nif (shareId && !shares[shareId]?.canWrite) {\n  throw new Error('Target share is read-only');\n}","typeGuard":"function isReadOnlyError(e: any): e is { code: 'isReadOnly' } {\n  return e && e.code === 'isReadOnly';\n}","tryCatchPattern":"try {\n  await fileApi.put(path, content, { shareId });\n} catch (e) {\n  if (isReadOnlyError(e)) {\n    // move item to a writable notebook, or show a user-facing notice\n    return { skipped: true, reason: 'read-only share' };\n  }\n  throw e;\n}","preventionTips":["Cache and refresh share permission flags client-side.","Disable editing UI for items in read-only shares.","Re-link the server account when permissions change server-side.","Avoid moving items into read-only shares programmatically."],"tags":["joplin-server","sync","permissions","read-only","share"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}