ExponentPushToken[token] is not a registered push notification recipient

Please provide the following:

  1. SDK Version: 4.12.1
  2. Platforms(Android/iOS/web/all): iOS
  3. Add the appropriate “Tag” based on what Expo library you have a question on.

I am using AWS Amplify on a React-Native app and have followed this tutorial which uses expo-server-sdk to send push notifications. I am getting this error in my Lambda function:

  {
    status: 'error',
    message: '"{type=expo, data=ExponentPushToken[nAvY4DCkl2lINEdIpdzLCV]}" is 
    not a registered push notification recipient',
    details: { error: 'DeviceNotRegistered' }
  }

Not sure what is going on here, first time using Expo push notifications.

My lambda code is as follows:

/* Amplify Params - DO NOT EDIT
You can access the following resource attributes as environment variables from your Lambda function
var environment = process.env.ENV
var region = process.env.REGION
var apiPushAPIGraphQLAPIIdOutput = process.env.API_PUSHAPI_GRAPHQLAPIIDOUTPUT
var apiPushAPIGraphQLAPIEndpointOutput = process.env.API_PUSHAPI_GRAPHQLAPIENDPOINTOUTPUT
Amplify Params - DO NOT EDIT */

const { Expo } = require("expo-server-sdk");

// Create a new Expo SDK client
let expo = new Expo();
exports.handler = function(event, context, callback) {
  try {
    let messages = [];
    // prettier-ignore
    for (var key in event.Endpoints) {
    if (event.Endpoints.hasOwnProperty(key)) {
      var endpoint = event.Endpoints[key];
        messages.push({
          to: String(endpoint.User.UserAttributes.expoToken),
          sound: "default",
          body: event.Message.apnsmessage.body,
           data: { "status": "ok" }
        });
    }
  }
    
    let chunks = expo.chunkPushNotifications(messages);
    let tickets = [];
    (async () => {
      for (let chunk of chunks) {
        try {
          let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
          console.log(ticketChunk);
          tickets.push(...ticketChunk);
        } catch (error) {
          console.error(error);
        }
      }
    })();
    let receiptIds = [];
    for (let ticket of tickets) {
      
      if (ticket.id) {
        receiptIds.push(ticket.id);
      }
    }
    let receiptIdChunks = expo.chunkPushNotificationReceiptIds(receiptIds);
    async () => {
      
      for (let chunk of receiptIdChunks) {
        try {
          let receipts = await expo.getPushNotificationReceiptsAsync(chunk);
          console.log(receipts);
          
          for (let receipt of receipts) {
            if (receipt.status === "ok") {
              continue;
            } else if (receipt.status === "error") {
              console.error(
                `There was an error sending a notification: ${receipt.message}`
              );
              if (receipt.details && receipt.details.error) {
               
                console.error(`The error code is ${receipt.details.error}`);
              }
            }
          }
        } catch (error) {
          console.error(error);
        }
      }
    };
    callback(null, event.Endpoints);
  } catch (error) {
    callback(error);
  }
};

If anyone was following the same tutorial, there is an error in the way the Expo Push Token is written to the API. The Token is fetched as "{type=expo, data=ExponentPushToken[nAvY4DCkl2lINEdIpdzLCV]}" and hence written as such. There is a snippet of code regarding the fetching of this token which is wrong in the tutorial docs under the file ./src/Main.js which looks as follows:

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      profile: {},
      message: "",
      user: ""
    };
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  async componentDidMount() {
    const user = await Auth.currentSession()
      .then(data => {
        this.setState({ user: data.idToken.payload.sub });
        return data.idToken.payload.sub;
      })
      .catch(err => console.log(err));
    const profile = await this.getUserProfile(user);
    // There is no expoToken available yet, so we will request that and save it into the profile
    if (profile.expoToken === null) {
      const { status } = await Notifications.requestPermissionsAsync();
      if (status !== "granted") {
        alert("No notification permissions!");
        return;
      }

      let token = await Notifications.getExpoPushTokenAsync();
      // Only update the profile with the expoToken if it not exists yet
      if (token !== "") {
        const inputParams = {
          id: user,
          expoToken: token
        };
        await API.graphql(
          graphqlOperation(mutations.updateUser, { input: inputParams })
        )
          .then(result => {
            console.log(result);
          })
          .catch(err => console.log(err));
      }
    }
  }

  async getUserProfile(sub) {
    const result = await API.graphql(
      graphqlOperation(queries.getUser, { id: sub })
    )
      .then(result => {
        this.setState({
          profile: result.data.getUser
        });
        return result.data.getUser;
      })
      .catch(err => console.log(err));
    return result;
  }

  async handleSubmit() {
    const inputParams = {
      message: this.state.message,
      token: this.state.profile.expoToken,
      name: this.state.profile.name,
      email: this.state.profile.email,
      id: this.state.user
    };
    await API.graphql(
      graphqlOperation(mutations.pinpoint, { input: inputParams })
    )
      .then(result => {
        console.log(result);
        console.log("success");
        this.setState({ message: "" });
      })
      .catch(err => console.log(err));
  }

  render() {
    return (
      <View style={{ marginTop: 80, marginLeft: 10, marginRight: 10 }}>
        <TextInput
          placeholder="Your push message"
          value={this.state.message}
          onChangeText={input => this.setState({ message: input })}
          style={{
            paddingLeft: 5,
            height: 40,
            fontSize: 16,
            marginBottom: 6,
            marginTop: 2
          }}
        ></TextInput>
        <Button title="Submit" onPress={this.handleSubmit} />
      </View>
    );
  }
}

export default Main;

You’ll notice that:

      let token = await Notifications.getExpoPushTokenAsync();
      // Only update the profile with the expoToken if it not exists yet
      if (token !== "") {
        const inputParams = {
          id: user,
          expoToken: token.data
        };
        await API.graphql(
          graphqlOperation(mutations.updateUser, { input: inputParams })
        )
          .then(result => {
            console.log(result);
          })
          .catch(err => console.log(err));
      }

Fetches the actual token as a string and remediates the downstream error.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.