shwenzhang/AndResGuard · error

Invalid config file: Missing required attribute

Error message

Invalid config file: Missing required attribute %s

What it means

readSignFromXml throws this IOException when a child element inside the <sign> section of the config XML has an empty or missing 'value' attribute. Every child of <sign> (path, keypass, storepass, alias) must carry a non-empty value because signing cannot proceed without them. The check runs before the tagName switch, so it applies to all sign sub-elements.

Solutions

  1. Fill in the value attribute for every child of <sign>: path, keypass, storepass, alias
  2. Pass the signing config via command-line arguments (--signatureFile, --keypass, etc.) instead of XML if you want to keep secrets out of the file
  3. Remove the <sign> issue entirely if you do not want to re-sign, letting the original signature be kept via useSignAPK=false

Example fix

// before (config.xml)
<issue id="sign">
  <path value=""/>
</issue>
// after (config.xml)
<issue id="sign">
  <path value="../signing/release.keystore"/>
  <storepass value="changeit"/>
  <keypass value="changeit"/>
  <alias value="release"/>
</issue>
Defensive patterns

Strategy: validation

Validate before calling

NodeList sign = doc.getElementsByTagName("sign");
if (sign.getLength() > 0) {
  NodeList children = ((Element) sign.item(0)).getChildNodes();
  for (int i = 0; i < children.getLength(); i++) {
    Node n = children.item(i);
    if (n.getNodeType() == Node.ELEMENT_NODE && ((Element) n).getAttribute("value").isEmpty()) {
      throw new IllegalStateException("<sign> child " + n.getNodeName() + " has empty value");
    }
  }
}

Try / catch

try {
  new Configuration(configFile, ...);
} catch (IOException e) {
  if (e.getMessage().contains("Missing required attribute value") && e.getStackTrace()[0].getMethodName().equals("readSignFromXml")) {
    // prompt for signing credentials or route them via CLI args
  }
  throw e;
}

Prevention

When it happens

Trigger: Config XML like <issue id="sign"><path value=""/></issue> or a child tag without a value attribute; readSignFromXml reads check.getAttribute("value"), finds length 0, and throws.

Common situations: Leaving the signing keypass or storepass value blank to avoid committing a password, then running the build; template sign sections with placeholder empty values; generated configs where the signing block was never populated.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of shwenzhang/AndResGuard@e4df245d82 (2026-09-12). Data as JSON: /api/errors/a11a9ce8caaf6902. Report an issue: GitHub.

Appendix: source

Thrown at AndResGuard-core/src/main/java/com/tencent/mm/resourceproguard/Configuration.java:320

  }

  private void readSignFromXml(Node node, File xmlConfigFileParentFile) throws IOException {
    if (mSignatureFile != null) {
      System.err.println("already set the sign info from command line, ignore this");
      return;
    }

    NodeList childNodes = node.getChildNodes();

    if (childNodes.getLength() > 0) {
      for (int j = 0, n = childNodes.getLength(); j < n; j++) {
        Node child = childNodes.item(j);
        if (child.getNodeType() == Node.ELEMENT_NODE) {
          Element check = (Element) child;
          String tagName = check.getTagName();
          String vaule = check.getAttribute(ATTR_VALUE);
          if (vaule.length() == 0) {
            throw new IOException(String.format("Invalid config file: Missing required attribute %s\n", ATTR_VALUE));
          }

          switch (tagName) {
            case ATTR_SIGNFILE_PATH:
              char ch = vaule.charAt(0);
              switch (ch) {
                // supports the writting style like ~/.android/debug.keystore. the symbol ~ represent the home directory of the current user.
                case '~':
                  mSignatureFile = new File(String.format("%s%s", System.getProperty("user.home"), vaule.substring(1)));
                  break;
                // relative to the directory of the xml config file.
                case '.':
                  mSignatureFile = new File(xmlConfigFileParentFile, vaule);
                  break;
                // keep the origin logical.
                default:
                  mSignatureFile = new File(vaule);
              }

View on GitHub (pinned to e4df245d82)