evernote-common-errors
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEvernote Common Errors
Evernote常见错误
Overview
概述
Comprehensive guide to diagnosing and resolving Evernote API errors, including EDAMUserException, EDAMSystemException, and EDAMNotFoundException.
本文是诊断和解决Evernote API错误的综合指南,涵盖EDAMUserException、EDAMSystemException和EDAMNotFoundException三种异常类型。
Prerequisites
前置条件
- Basic Evernote SDK setup
- Understanding of Evernote data model
- 基础的Evernote SDK配置
- 了解Evernote数据模型
Error Types
错误类型
Evernote uses three main exception types:
| Exception | When Thrown |
|---|---|
| Client error - invalid input, permissions |
| Server error - rate limits, maintenance |
| Resource not found - invalid GUID |
Evernote主要使用三种异常类型:
| 异常类型 | 触发场景 |
|---|---|
| 客户端错误 - 输入无效、权限不足 |
| 服务器错误 - 速率限制、维护中 |
| 资源不存在 - GUID无效 |
EDAMUserException Errors
EDAMUserException错误
BAD_DATA_FORMAT
BAD_DATA_FORMAT
Cause: Invalid ENML content or malformed data
javascript
// Error
{
errorCode: 1, // BAD_DATA_FORMAT
parameter: 'Note.content'
}
// Common causes and fixes:
// 1. Missing XML declaration
// Wrong:
'<en-note><p>Hello</p></en-note>'
// Correct:
`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note><p>Hello</p></en-note>`
// 2. Forbidden HTML elements
// Wrong: contains <script>
`<en-note><script>alert('hi')</script></en-note>`
// Correct: remove scripts
`<en-note><p>Content only</p></en-note>`
// 3. Unclosed tags
// Wrong:
'<en-note><p>Hello<br></en-note>'
// Correct:
'<en-note><p>Hello</p><br/></en-note>'Fix: Validate ENML before sending:
javascript
function validateENML(content) {
const errors = [];
// Required declarations
if (!content.includes('<?xml version="1.0"')) {
errors.push('Missing XML declaration');
}
if (!content.includes('<!DOCTYPE en-note')) {
errors.push('Missing DOCTYPE');
}
if (!content.includes('<en-note>')) {
errors.push('Missing <en-note> root element');
}
// Forbidden elements
const forbidden = [
/<script/i, /<form/i, /<input/i, /<button/i,
/<iframe/i, /<object/i, /<embed/i, /<applet/i
];
forbidden.forEach(pattern => {
if (pattern.test(content)) {
errors.push(`Forbidden element: ${pattern.source}`);
}
});
// Forbidden attributes
if (/\s(class|id|onclick|onload|onerror)=/i.test(content)) {
errors.push('Forbidden attributes (class, id, event handlers)');
}
return { valid: errors.length === 0, errors };
}原因: ENML内容无效或数据格式错误
javascript
// Error
{
errorCode: 1, // BAD_DATA_FORMAT
parameter: 'Note.content'
}
// Common causes and fixes:
// 1. Missing XML declaration
// Wrong:
'<en-note><p>Hello</p></en-note>'
// Correct:
`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note><p>Hello</p></en-note>`
// 2. Forbidden HTML elements
// Wrong: contains <script>
`<en-note><script>alert('hi')</script></en-note>`
// Correct: remove scripts
`<en-note><p>Content only</p></en-note>`
// 3. Unclosed tags
// Wrong:
'<en-note><p>Hello<br></en-note>'
// Correct:
'<en-note><p>Hello</p><br/></en-note>'修复方案: 在发送前验证ENML:
javascript
function validateENML(content) {
const errors = [];
// Required declarations
if (!content.includes('<?xml version="1.0"')) {
errors.push('Missing XML declaration');
}
if (!content.includes('<!DOCTYPE en-note')) {
errors.push('Missing DOCTYPE');
}
if (!content.includes('<en-note>')) {
errors.push('Missing <en-note> root element');
}
// Forbidden elements
const forbidden = [
/<script/i, /<form/i, /<input/i, /<button/i,
/<iframe/i, /<object/i, /<embed/i, /<applet/i
];
forbidden.forEach(pattern => {
if (pattern.test(content)) {
errors.push(`Forbidden element: ${pattern.source}`);
}
});
// Forbidden attributes
if (/\s(class|id|onclick|onload|onerror)=/i.test(content)) {
errors.push('Forbidden attributes (class, id, event handlers)');
}
return { valid: errors.length === 0, errors };
}DATA_REQUIRED
DATA_REQUIRED
Cause: Missing required field
javascript
// Error
{
errorCode: 2, // DATA_REQUIRED
parameter: 'Note.title'
}
// Fix: Ensure required fields are set
const note = new Evernote.Types.Note();
note.title = 'Required Title'; // Cannot be null or empty
note.content = validENMLContent; // Cannot be null原因: 缺少必填字段
javascript
// Error
{
errorCode: 2, // DATA_REQUIRED
parameter: 'Note.title'
}
// Fix: Ensure required fields are set
const note = new Evernote.Types.Note();
note.title = 'Required Title'; // Cannot be null or empty
note.content = validENMLContent; // Cannot be null修复方案: 确保必填字段已设置
PERMISSION_DENIED
PERMISSION_DENIED
Cause: API key lacks required permissions
javascript
// Error
{
errorCode: 3, // PERMISSION_DENIED
parameter: 'NoteStore.shareNote'
}
// Causes:
// 1. API key doesn't have sharing permission
// 2. Trying to access business features without business API key
// 3. Accessing notes in shared notebooks without permission
// Fix: Request appropriate permissions when creating API key
// See: https://dev.evernote.com/doc/articles/permissions.php原因: API密钥缺少必要权限
javascript
// Error
{
errorCode: 3, // PERMISSION_DENIED
parameter: 'NoteStore.shareNote'
}
// Causes:
// 1. API key doesn't have sharing permission
// 2. Trying to access business features without business API key
// 3. Accessing notes in shared notebooks without permission
// Fix: Request appropriate permissions when creating API key
// See: https://dev.evernote.com/doc/articles/permissions.php修复方案: 创建API密钥时申请相应权限
// 参考:https://dev.evernote.com/doc/articles/permissions.php
INVALID_AUTH
INVALID_AUTH
Cause: Invalid or expired authentication token
javascript
// Error
{
errorCode: 4, // INVALID_AUTH
parameter: 'authenticationToken'
}
// Fix: Check token validity
async function checkTokenValidity(client) {
try {
const userStore = client.getUserStore();
await userStore.getUser();
return { valid: true };
} catch (error) {
if (error.errorCode === 4) {
return {
valid: false,
reason: 'Token expired or revoked',
action: 'Re-authenticate via OAuth'
};
}
throw error;
}
}原因: 认证令牌无效或已过期
javascript
// Error
{
errorCode: 4, // INVALID_AUTH
parameter: 'authenticationToken'
}
// Fix: Check token validity
async function checkTokenValidity(client) {
try {
const userStore = client.getUserStore();
await userStore.getUser();
return { valid: true };
} catch (error) {
if (error.errorCode === 4) {
return {
valid: false,
reason: 'Token expired or revoked',
action: 'Re-authenticate via OAuth'
};
}
throw error;
}
}修复方案: 检查令牌有效性
AUTH_EXPIRED
AUTH_EXPIRED
Cause: Token has passed expiration date
javascript
// Error
{
errorCode: 5, // AUTH_EXPIRED
parameter: 'authenticationToken'
}
// Tokens expire after 1 year by default
// Users can set shorter: 1 day, 1 week, 1 month
// Fix: Store and check expiration
function isTokenExpired(expirationTimestamp) {
return Date.now() > expirationTimestamp;
}
// When authenticating, save edam_expires:
// results.edam_expires contains expiration timestamp原因: 令牌已过有效期
javascript
// Error
{
errorCode: 5, // AUTH_EXPIRED
parameter: 'authenticationToken'
}
// Tokens expire after 1 year by default
// Users can set shorter: 1 day, 1 week, 1 month
// Fix: Store and check expiration
function isTokenExpired(expirationTimestamp) {
return Date.now() > expirationTimestamp;
}
// When authenticating, save edam_expires:
// results.edam_expires contains expiration timestamp修复方案: 存储并检查过期时间
// 认证时,保存edam_expires字段:
// results.edam_expires包含过期时间戳
LIMIT_REACHED
LIMIT_REACHED
Cause: Account limits exceeded
javascript
// Error
{
errorCode: 6, // LIMIT_REACHED
parameter: 'Notebook.name'
}
// Account limits:
// - 250 notebooks max
// - 100,000 tags max
// - 100,000 notes max (business accounts)
// - Upload limits per month
// Fix: Check limits before creating
async function canCreateNotebook(noteStore) {
const notebooks = await noteStore.listNotebooks();
return notebooks.length < 250;
}原因: 超出账户限制
javascript
// Error
{
errorCode: 6, // LIMIT_REACHED
parameter: 'Notebook.name'
}
// Account limits:
// - 250 notebooks max
// - 100,000 tags max
// - 100,000 notes max (business accounts)
// - Upload limits per month
// Fix: Check limits before creating
async function canCreateNotebook(noteStore) {
const notebooks = await noteStore.listNotebooks();
return notebooks.length < 250;
}修复方案: 创建前检查账户限制
QUOTA_REACHED
QUOTA_REACHED
Cause: Monthly upload quota exceeded
javascript
// Error
{
errorCode: 7, // QUOTA_REACHED
parameter: 'Note.content'
}
// Quotas vary by account type:
// - Basic: 60 MB/month
// - Premium: 10 GB/month
// - Business: 20 GB/month (per user)
// Check remaining quota
async function getRemainingQuota(userStore) {
const user = await userStore.getUser();
const accounting = user.accounting;
return {
uploadLimit: accounting.uploadLimit,
uploadLimitEnd: new Date(accounting.uploadLimitEnd),
uploaded: accounting.uploaded,
remaining: accounting.uploadLimit - accounting.uploaded
};
}原因: 超出月度上传配额
javascript
// Error
{
errorCode: 7, // QUOTA_REACHED
parameter: 'Note.content'
}
// Quotas vary by account type:
// - Basic: 60 MB/month
// - Premium: 10 GB/month
// - Business: 20 GB/month (per user)
// Check remaining quota
async function getRemainingQuota(userStore) {
const user = await userStore.getUser();
const accounting = user.accounting;
return {
uploadLimit: accounting.uploadLimit,
uploadLimitEnd: new Date(accounting.uploadLimitEnd),
uploaded: accounting.uploaded,
remaining: accounting.uploadLimit - accounting.uploaded
};
}修复方案: 查询剩余配额
EDAMSystemException Errors
EDAMSystemException错误
RATE_LIMIT_REACHED
RATE_LIMIT_REACHED
Cause: Too many API calls per hour
javascript
// Error
{
errorCode: 19, // RATE_LIMIT_REACHED
rateLimitDuration: 300 // seconds until reset
}
// Fix: Implement exponential backoff
async function withRateLimitRetry(operation, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (error.errorCode === 19 && error.rateLimitDuration) {
console.log(`Rate limited. Waiting ${error.rateLimitDuration}s...`);
await sleep(error.rateLimitDuration * 1000);
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));原因: 每小时API调用次数过多
javascript
// Error
{
errorCode: 19, // RATE_LIMIT_REACHED
rateLimitDuration: 300 // seconds until reset
}
// Fix: Implement exponential backoff
async function withRateLimitRetry(operation, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (error.errorCode === 19 && error.rateLimitDuration) {
console.log(`Rate limited. Waiting ${error.rateLimitDuration}s...`);
await sleep(error.rateLimitDuration * 1000);
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));修复方案: 实现指数退避重试机制
SYSTEM_MAINTENANCE
SYSTEM_MAINTENANCE
Cause: Evernote service is under maintenance
javascript
// Error
{
errorCode: 1, // UNKNOWN (with message about maintenance)
message: 'Service temporarily unavailable'
}
// Fix: Retry with backoff
async function withMaintenanceRetry(operation) {
const delays = [1000, 5000, 15000, 60000]; // Progressive delays
for (const delay of delays) {
try {
return await operation();
} catch (error) {
if (error.message?.includes('temporarily unavailable')) {
console.log(`Service maintenance. Retrying in ${delay / 1000}s...`);
await sleep(delay);
continue;
}
throw error;
}
}
throw new Error('Service unavailable - maintenance ongoing');
}原因: Evernote服务正在维护
javascript
// Error
{
errorCode: 1, // UNKNOWN (with message about maintenance)
message: 'Service temporarily unavailable'
}
// Fix: Retry with backoff
async function withMaintenanceRetry(operation) {
const delays = [1000, 5000, 15000, 60000]; // Progressive delays
for (const delay of delays) {
try {
return await operation();
} catch (error) {
if (error.message?.includes('temporarily unavailable')) {
console.log(`Service maintenance. Retrying in ${delay / 1000}s...`);
await sleep(delay);
continue;
}
throw error;
}
}
throw new Error('Service unavailable - maintenance ongoing');
}修复方案: 退避重试
EDAMNotFoundException Errors
EDAMNotFoundException错误
Cause: Referenced resource doesn't exist
javascript
// Error
{
identifier: 'Note.guid',
key: '12345678-abcd-1234-efgh-invalid00000'
}
// Common scenarios:
// 1. Note was deleted
// 2. Note is in trash
// 3. Invalid GUID format
// 4. Note belongs to different user
// Fix: Handle gracefully
async function safeGetNote(noteStore, guid) {
try {
return await noteStore.getNote(guid, true, false, false, false);
} catch (error) {
if (error.identifier === 'Note.guid') {
console.log(`Note not found: ${guid}`);
return null;
}
throw error;
}
}原因: 引用的资源不存在
javascript
// Error
{
identifier: 'Note.guid',
key: '12345678-abcd-1234-efgh-invalid00000'
}
// Common scenarios:
// 1. Note was deleted
// 2. Note is in trash
// 3. Invalid GUID format
// 4. Note belongs to different user
// Fix: Handle gracefully
async function safeGetNote(noteStore, guid) {
try {
return await noteStore.getNote(guid, true, false, false, false);
} catch (error) {
if (error.identifier === 'Note.guid') {
console.log(`Note not found: ${guid}`);
return null;
}
throw error;
}
}修复方案: 优雅处理错误
Error Handling Service
错误处理服务
javascript
// services/error-handler.js
const Evernote = require('evernote');
class EvernoteErrorHandler {
static handle(error) {
// EDAMUserException
if (error.errorCode !== undefined && error.parameter !== undefined) {
return this.handleUserException(error);
}
// EDAMSystemException
if (error.errorCode !== undefined && error.rateLimitDuration !== undefined) {
return this.handleSystemException(error);
}
// EDAMNotFoundException
if (error.identifier !== undefined) {
return this.handleNotFoundException(error);
}
// Unknown error
return {
type: 'UNKNOWN',
message: error.message || 'Unknown Evernote error',
recoverable: false,
original: error
};
}
static handleUserException(error) {
const codes = {
1: { name: 'BAD_DATA_FORMAT', action: 'Validate input data format' },
2: { name: 'DATA_REQUIRED', action: 'Provide required field' },
3: { name: 'PERMISSION_DENIED', action: 'Check API key permissions' },
4: { name: 'INVALID_AUTH', action: 'Re-authenticate user' },
5: { name: 'AUTH_EXPIRED', action: 'Token expired, re-authenticate' },
6: { name: 'LIMIT_REACHED', action: 'Account limit exceeded' },
7: { name: 'QUOTA_REACHED', action: 'Upload quota exceeded' }
};
const info = codes[error.errorCode] || { name: 'UNKNOWN', action: 'Check documentation' };
return {
type: 'USER_EXCEPTION',
code: error.errorCode,
name: info.name,
parameter: error.parameter,
action: info.action,
recoverable: [4, 5].includes(error.errorCode),
original: error
};
}
static handleSystemException(error) {
return {
type: 'SYSTEM_EXCEPTION',
code: error.errorCode,
rateLimitDuration: error.rateLimitDuration,
action: `Wait ${error.rateLimitDuration} seconds before retrying`,
recoverable: true,
original: error
};
}
static handleNotFoundException(error) {
return {
type: 'NOT_FOUND',
identifier: error.identifier,
key: error.key,
action: 'Resource does not exist or was deleted',
recoverable: false,
original: error
};
}
}
module.exports = EvernoteErrorHandler;javascript
// services/error-handler.js
const Evernote = require('evernote');
class EvernoteErrorHandler {
static handle(error) {
// EDAMUserException
if (error.errorCode !== undefined && error.parameter !== undefined) {
return this.handleUserException(error);
}
// EDAMSystemException
if (error.errorCode !== undefined && error.rateLimitDuration !== undefined) {
return this.handleSystemException(error);
}
// EDAMNotFoundException
if (error.identifier !== undefined) {
return this.handleNotFoundException(error);
}
// Unknown error
return {
type: 'UNKNOWN',
message: error.message || 'Unknown Evernote error',
recoverable: false,
original: error
};
}
static handleUserException(error) {
const codes = {
1: { name: 'BAD_DATA_FORMAT', action: 'Validate input data format' },
2: { name: 'DATA_REQUIRED', action: 'Provide required field' },
3: { name: 'PERMISSION_DENIED', action: 'Check API key permissions' },
4: { name: 'INVALID_AUTH', action: 'Re-authenticate user' },
5: { name: 'AUTH_EXPIRED', action: 'Token expired, re-authenticate' },
6: { name: 'LIMIT_REACHED', action: 'Account limit exceeded' },
7: { name: 'QUOTA_REACHED', action: 'Upload quota exceeded' }
};
const info = codes[error.errorCode] || { name: 'UNKNOWN', action: 'Check documentation' };
return {
type: 'USER_EXCEPTION',
code: error.errorCode,
name: info.name,
parameter: error.parameter,
action: info.action,
recoverable: [4, 5].includes(error.errorCode),
original: error
};
}
static handleSystemException(error) {
return {
type: 'SYSTEM_EXCEPTION',
code: error.errorCode,
rateLimitDuration: error.rateLimitDuration,
action: `Wait ${error.rateLimitDuration} seconds before retrying`,
recoverable: true,
original: error
};
}
static handleNotFoundException(error) {
return {
type: 'NOT_FOUND',
identifier: error.identifier,
key: error.key,
action: 'Resource does not exist or was deleted',
recoverable: false,
original: error
};
}
}
module.exports = EvernoteErrorHandler;Usage Example
使用示例
javascript
const ErrorHandler = require('./services/error-handler');
async function createNoteSafely(noteStore, note) {
try {
return await noteStore.createNote(note);
} catch (error) {
const handled = ErrorHandler.handle(error);
console.error('Evernote error:', handled.name || handled.type);
console.error('Parameter:', handled.parameter || handled.identifier);
console.error('Action:', handled.action);
if (handled.recoverable) {
console.log('Error is recoverable');
if (handled.rateLimitDuration) {
await sleep(handled.rateLimitDuration * 1000);
return noteStore.createNote(note);
}
}
throw error;
}
}javascript
const ErrorHandler = require('./services/error-handler');
async function createNoteSafely(noteStore, note) {
try {
return await noteStore.createNote(note);
} catch (error) {
const handled = ErrorHandler.handle(error);
console.error('Evernote error:', handled.name || handled.type);
console.error('Parameter:', handled.parameter || handled.identifier);
console.error('Action:', handled.action);
if (handled.recoverable) {
console.log('Error is recoverable');
if (handled.rateLimitDuration) {
await sleep(handled.rateLimitDuration * 1000);
return noteStore.createNote(note);
}
}
throw error;
}
}Output
输出结果
- Understanding of all Evernote exception types
- Error code reference with solutions
- Reusable error handling service
- Rate limit retry implementation
- 了解所有Evernote异常类型
- 带解决方案的错误码参考
- 可复用的错误处理服务
- 速率限制重试实现
Quick Reference
快速参考
| Code | Exception | Cause | Fix |
|---|---|---|---|
| 1 | UserException | Bad data format | Validate ENML |
| 2 | UserException | Missing required field | Add required field |
| 3 | UserException | Permission denied | Check API key |
| 4 | UserException | Invalid auth | Re-authenticate |
| 5 | UserException | Auth expired | Refresh token |
| 6 | UserException | Limit reached | Check account limits |
| 7 | UserException | Quota reached | Check upload quota |
| 19 | SystemException | Rate limit | Wait rateLimitDuration |
| - | NotFoundException | GUID not found | Verify resource exists |
| 错误码 | 异常类型 | 原因 | 修复方案 |
|---|---|---|---|
| 1 | UserException | 数据格式错误 | 验证ENML |
| 2 | UserException | 缺少必填字段 | 添加必填字段 |
| 3 | UserException | 权限不足 | 检查API密钥 |
| 4 | UserException | 认证无效 | 重新认证 |
| 5 | UserException | 认证过期 | 刷新令牌 |
| 6 | UserException | 超出账户限制 | 检查账户限制 |
| 7 | UserException | 超出上传配额 | 检查上传配额 |
| 19 | SystemException | 速率限制 | 等待rateLimitDuration秒后重试 |
| - | NotFoundException | GUID不存在 | 验证资源是否存在 |
Resources
参考资源
Next Steps
下一步
For debugging tools and techniques, see .
evernote-debug-bundle如需调试工具和技术,请查看。
evernote-debug-bundle