Skip to main content
Triggered when Smartcar encounters an error while attempting to retrieve signal data from a vehicle. This event helps you monitor data availability, understand missing or stale signal data and provide an event to trigger a user action to resolve the issue.

When This Event Fires

The VEHICLE_ERROR event fires when:
  • Smartcar detects an error while retrieving signal data
  • The vehicle manufacturer’s API returns an error
  • The vehicle is offline or unreachable
  • Authentication, permission, or compatibility errors occur
Resolution notifications: When an error condition is resolved (e.g., vehicle comes back online), Smartcar sends another VEHICLE_ERROR event with state set to "RESOLVED". This enables automatic recovery workflows.

Error Categories

Errors are categorized by type and include specific error codes:
Issues with the user’s account with the vehicle manufacturer.
TypeCodeDescription
CONNECTED_SERVICES_ACCOUNTACCOUNT_ISSUEGeneral account problem
CONNECTED_SERVICES_ACCOUNTAUTHENTICATION_FAILEDAuthentication credentials are invalid
CONNECTED_SERVICES_ACCOUNTPERMISSIONUser lacks necessary permissions
CONNECTED_SERVICES_ACCOUNTSUBSCRIPTIONRequired subscription is inactive or expired
CONNECTED_SERVICES_ACCOUNTVIRTUAL_KEY_REQUIREDVehicle requires a virtual key to be configured
Issues with the vehicle’s connectivity or state.
TypeCodeDescription
VEHICLE_STATEREMOTE_ACCESS_DISABLEDRemote access is disabled on the vehicle
VEHICLE_STATEASLEEPVehicle is in sleep mode
VEHICLE_STATEUNREACHABLEVehicle is not connected to the internet
Issues with signal availability for the specific vehicle.
TypeCodeDescription
COMPATIBILITYMAKE_NOT_COMPATIBLEVehicle manufacturer doesn’t support this signal
COMPATIBILITYSMARTCAR_NOT_CAPABLESmartcar cannot retrieve this signal yet
COMPATIBILITYVEHICLE_NOT_CAPABLEThis specific vehicle doesn’t support the signal
Issues with granted permissions.
TypeCodeDescription
PERMISSIONnullRequired permission not granted by user

Payload Structure

eventId
string
required
Unique identifier for this event. Use this for idempotency to prevent duplicate processing.
eventType
string
required
Always "VEHICLE_ERROR" for this event type.
vehicleId
string
required
Smartcar vehicle ID for the vehicle this event relates to.
data
object
required
Container for event data.
meta
object
required
Webhook delivery metadata. See Event Reference Overview for complete meta object schema.

Example Payloads

  • Error State
  • Resolved State
Delivered when an error occurs.
{
  "eventId": "5a537912-9ad3-424b-ba33-65a1704567e9",
  "eventType": "VEHICLE_ERROR",
  "vehicleId": "123e4567-e89b-12d3-a456-426614174000",
  "data": {
    "user": {
      "id": "93b3ea96-ca37-43a9-9073-f4334719iok7"
    },
    "vehicle": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "make": "TESLA",
      "model": "Model 3",
      "year": 2020
    },
    "errors": [
      {
        "type": "COMPATIBILITY",
        "code": "VEHICLE_NOT_CAPABLE",
        "state": "ERROR",
        "description": "The vehicle is incapable of performing your request.",
        "suggestedUserMessage": "Your car is unable to perform this request.",
        "docURL": "https://smartcar.com/docs/errors/api-errors/compatibility-errors#vehicle-not-capable",
        "resolution": {
          "type": "CONTACT_SUPPORT"
        },
        "signals": [
          {
            "code": "location-preciselocation",
            "name": "PreciseLocation",
            "group": "Location"
          },
          {
            "code": "tractionbattery-stateofcharge",
            "name": "StateOfCharge",
            "group": "TractionBattery"
          }
        ]
      }
    ]
  },
  "meta": {
    "version": "4.0",
    "deliveryId": "48b25f8f-9fea-42e1-9085-81043682cbb8",
    "deliveredAt": 1761896351529,
    "webhookId": "123e4567-e89b-12d3-a456-426614174000",
    "webhookName": "Battery Monitoring",
    "mode": "LIVE"
  }
}

Common Error Scenarios

ScenarioTypeCodeRecommended Action
Vehicle offlineVEHICLE_STATEUNREACHABLERetry later; notify user if persistent
User revoked permissionsCONNECTED_SERVICES_ACCOUNTPERMISSIONPrompt user to re-authenticate
Connected services subscription expiredCONNECTED_SERVICES_ACCOUNTSUBSCRIPTIONDirect user to renew manufacturer subscription
Signal not supportedCOMPATIBILITYVEHICLE_NOT_CAPABLERemove signal from webhook or handle gracefully
Vehicle asleepVEHICLE_STATEASLEEPRetry later; avoid waking vehicle unnecessarily

Processing VEHICLE_ERROR Events

Basic Handler

function handleVehicleError(payload) {
  const { vehicleId, data } = payload;
  const { errors } = data;
  
  errors.forEach(error => {
    if (error.state === 'ERROR') {
      // New error
      logError(vehicleId, error);
      notifyUser(vehicleId, error.suggestedUserMessage);
      
      // Take action based on error type
      if (error.resolution.type === 'RETRY_LATER') {
        scheduleRetry(vehicleId, error.signals);
      } else if (error.resolution.type === 'USER_ACTION_REQUIRED') {
        sendUserNotification(vehicleId, error);
      }
    } else if (error.state === 'RESOLVED') {
      // Error resolved
      logResolution(vehicleId, error);
      clearErrorState(vehicleId, error.code);
      notifyUser(vehicleId, 'Issue resolved, data is now available');
    }
  });
}

Best Practices

Monitor both ERROR and RESOLVED states to implement automatic recovery workflows.
async function trackErrorState(payload) {
  const { vehicleId, data } = payload;
  
  for (const error of data.errors) {
    if (error.state === 'ERROR') {
      await db.errors.insert({
        vehicleId,
        errorType: error.type,
        errorCode: error.code,
        affectedSignals: error.signals.map(s => s.code),
        detectedAt: new Date()
      });
    } else if (error.state === 'RESOLVED') {
      await db.errors.update(
        { vehicleId, errorCode: error.code },
        { resolvedAt: new Date(), status: 'resolved' }
      );
    }
  }
}
Display the user-friendly message to vehicle owners instead of technical error descriptions.
function notifyVehicleOwner(error, userId) {
  const message = error.suggestedUserMessage || 
    'We encountered an issue retrieving data from your vehicle.';
  
  sendNotification(userId, {
    title: 'Vehicle Data Unavailable',
    body: message,
    link: error.docURL // Link to troubleshooting docs
  });
}
Some errors (ASLEEP, UNREACHABLE) are transient. Implement exponential backoff retries.
const TRANSIENT_ERROR_CODES = ['ASLEEP', 'UNREACHABLE'];

function shouldRetry(error) {
  return TRANSIENT_ERROR_CODES.includes(error.code);
}

async function handleTransientError(vehicleId, error) {
  const retryDelays = [60000, 300000, 900000]; // 1m, 5m, 15m
  
  for (const delay of retryDelays) {
    await sleep(delay);
    
    try {
      // Attempt to fetch data again
      const data = await smartcar.fetchSignals(vehicleId, error.signals);
      return data; // Success
    } catch (err) {
      // Still failing, continue retrying
    }
  }
  
  // All retries failed
  notifyUser(vehicleId, 'Unable to reach vehicle after multiple attempts');
}
A single VEHICLE_ERROR event can contain multiple errors affecting different signals.
function categorizeErrors(errors) {
  const categorized = {
    transient: [],
    userAction: [],
    permanent: []
  };
  
  errors.forEach(error => {
    if (['ASLEEP', 'UNREACHABLE'].includes(error.code)) {
      categorized.transient.push(error);
    } else if (error.resolution.type === 'USER_ACTION_REQUIRED') {
      categorized.userAction.push(error);
    } else {
      categorized.permanent.push(error);
    }
  });
  
  return categorized;
}
When errors occur, show stale data with a clear indication that it’s not current.
function displayVehicleData(vehicleId, hasActiveErrors) {
  const data = getLastKnownData(vehicleId);
  
  return {
    ...data,
    isStale: hasActiveErrors,
    staleSince: getLastSuccessfulFetch(vehicleId),
    errorMessage: 'Unable to retrieve current data. Showing last known values.'
  };
}

Error Resolution Tracking

When an error condition is resolved, Smartcar sends a VEHICLE_ERROR event with state: "RESOLVED":
function handleErrorResolution(payload) {
  const { vehicleId, data } = payload;
  
  data.errors.forEach(error => {
    if (error.state === 'RESOLVED') {
      console.log(`Error resolved for ${vehicleId}: ${error.code}`);
      
      // Clear error flags
      clearVehicleError(vehicleId, error.code);
      
      // Resume data collection
      resumeDataCollection(vehicleId, error.signals);
      
      // Notify user
      notifyUser(vehicleId, 'Your vehicle is now connected and data is available');
    }
  });
}
Automatic recovery: Use state: "RESOLVED" events to automatically resume normal operations without manual intervention.

Next Steps